LoginSignup
7
7

More than 5 years have passed since last update.

非同期関数のcallbackのテストを行う

Posted at

最近ハマっているcoffeescriptを使って説明します。

非同期な関数

google maps apiにはGeocoderなど、座標から地名を取得するような関数でサーバからの応答を待つ非同期関数が存在する。例えば下記のgeocode()関数が非同期な関数である。

geocode.coffee
class @Geocoder
  constructor: ->
    @geocoder = new google.maps.Geocoder()
    @results

  getPlaceName: (point) ->
    request = {
      language : "en"
      latLng : point
    }

    geocoder.geocode(
      request
      (results, status) =>
        # ここがサーバからの応答があってから実行されるcallback
        @results = results

上記のcallback関数の実行後のテストをjsTestDriverを使って行いたい場合どうすればいいか?
sinon.jsを使って、擬似サーバによりテストを行う方法もあるが、今回の場合googleの応答結果を使ってテストしたいのでやり方を考えてみた。

jQuery Deferredを利用する

最初にsetTimeoutで少し待ってからテストを行えばいいと考えていたが、その方法ではうまく行かなかった。うまく行ったのは、AsyncTestCaseとjQuery Deferredを用いてテストする方法。
AsyncTestCaseについては先日の投稿にも書いた通りである。

CoffeeScriptでAsyncTestCaseを書く

また、jQuery Deferredについては下記を参考にした。

jQuery.Deferredを使って楽しい非同期生活を送る方法

GeocoderクラスをDeferredを使って書き換える

というわけで、最初に書いたクラスをDeferredを用いて書き換えてみる。

geocode.coffee
class @Geocoder
  constructor: ->
    @geocoder = new google.maps.Geocoder()
    @results

  getPlaceName: (point) ->
    defer = new $.Deferred()  #追加

    request = {
      language : "en"
      latLng : point
    }

    geocoder.geocode(
      request
      (results, status) =>
        # ここがサーバからの応答があってから実行されるcallback
        @results = results
        defer.resolve()       #追加

    return defer.promise()   #追加

テストケースを書いてみる

geocode_test.coffee
  AsyncTestCase "Geocoder Test",
    setUp: (queue)->
      queue.call(
        "prepare google map"
        (callbacks) ->
          onReadyGoogleMap = callbacks.add(
            =>
              @geocoder = new Geocoder()
          )

          $.when(
            loadGoogleMaps(3, null, "en", false)
          )
          .done(
              ->
                onReadyGoogleMap()
          )
      )

    "test getPlaceName": (queue) ->
      queue.call(
        "wait function callback"
        (callbacks) ->
          success = callbacks.add(
            =>
              assertNotEquals(0, @geocoder.results.length) #ここにテストケースを書く
          )

          $.when(
            @geocoder.getPlaceName(new google.maps.LatLng(37.4815, -122.2287))
          )
          .done(
            ->
              success()
          )
      )
  • setUp部分は、上にも書いたが先日書いた投稿の通り。こうしないとgoogleのapiのテストができない。

CoffeeScriptでAsyncTestCaseを書く

  • "test getPlaceName"のテスト部分も、google apiのロードとやっていることは同じ。
  • callbacks.addでcallback実行後のテストケースを予め登録しておく。こうしておくと、success()が実行されるまで、次のテストの実行を待機するようになる。
  • getPlaceName()をjQuery deferred.promise()を返すように書き換えたので、$.whenで待つようにすることができる。
  • geocode()のcallback内でdeferred.resolve()が実行されると、上記テストケースの.done()が実行される。
  • success()のテストが実行される。
7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7