AngularJS Jasmine 2.0异步测试超时

时间:2014-07-31 19:04:06

标签: angularjs coffeescript jasmine phantomjs pouchdb

我知道这已被多次询问过,我已经查看了其他问题并遵循了这些问题,但我似乎无法解决这个问题。

基本上,我在服务中有一个函数将数据放入pouchDB。函数addTask将返回一个promise,它将在数据库插入成功时作为结果值解析。

这在浏览器环境中的手动测试期间工作正常,但由于超时而在Jasmine测试期间失败。

Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

似乎在规范的then中作为参数传递的回调永远不会运行。

JSBin

app = angular.module 'testApp', ['ngMock']

app.service 'Pouch', ($q) ->
  db = new PouchDB 'tasks'

  return {
    addTask : (task) ->
        deferred = $q.defer()
        db.put task, (task.title + task.due), (err, res) ->
          console.log res # Both prints fine
          console.log err
          deferred.resolve res
        return deferred.promise
  }

describe 'Service: Pouch', ->
  Pouch = {}
  $rootScope = {}

  beforeEach () ->
    module 'testApp'
    PouchDB.destroy 'tasks'
    inject (_Pouch_, _$rootScope_) ->
      Pouch = _Pouch_
      $rootScope = _$rootScope_

  value = undefined

  testTask =
    type: 'TASK'
    title: 'Feed the kitten'
    due: 201120141900
    group: ['TODAY', 'TOMORROW']

  it 'should add task upon request', (done) ->
    promise = Pouch.addTask testTask
    promise.then (result) ->
      # Never reached here
      expect(result.ok).toBe(true)
      done()
    $rootScope.$apply() # I don't think this is neccessary.

我该怎么办?我也尝试使用$timeout,但它没有用。

3 个答案:

答案 0 :(得分:2)

这看起来是我能想到的两件事之一:

  1. 要么:你似乎没有孤立地测试你的Pouch服务。什么是异步调用?它在做什么?有没有可以模拟并注入的内容,以便您可以使用$q.when()$timeout.$flush()进行测试?
  2. ...或...您的意思是这是一个集成测试,在这种情况下,您可以使用jasmine. DEFAULT_TIMEOUT_INTERVALbeforeEachafterEach设置为更高的数字
  3. 编辑:您的服务,如您的帖子中所写的和接受的答案无法单独测试。

    如果要单独测试,则需要将PouchDB注入依赖项。您无需测试 Pouch ,只需测试您自己的代码:

    # Create a service that gets PouchDB from $window
    app.service 'PouchDB', ($window) ->
        return $window.PouchDB
    
    # Now you can inject it as a dependency!
    app.service 'Pouch', ($q, $rootScope, PouchDB) -> 
        # *now* new it up.
        db = new PouchDB 'tasks'
    
        return addTask: (task) ->
            deferred = $q.defer()
    
            db.put task, (task.title + task.due), (err, res) ->
              $rootScope.$apply ->  deferred.resolve res
    
            deferred.promise
    

    您的测试现已被隔离:

    describe 'Service: Pouch', ->
      Pouch = undefined
      $rootScope = undefined
      PouchDBMock = undefined
      $timeout = undefined
      value = undefined
    
      beforeEach () ->
        module 'testApp', ($provide) -> 
          # create your mock
          PouchDBMock = jasmine.createSpyObj('PouchDB', ['put', 'get', 'etc']);
    
          # provide it to the container
          $provide.value 'PouchDB', PouchDBMock
    
    
        # No longer necessary, because you're isolated!
        # ---> PouchDB.destroy 'tasks' <---
    
        # Now when you inject your Pouch service, it will have
        # our PouchDBMock being used inside of it.
        # also inject $timeout so you can flush() later
        inject (_Pouch_, _$rootScope_, _$timeout_) ->
          Pouch = _Pouch_
          $rootScope = _$rootScope_
          $timeout = _$timeout_
    
        # MOVED: this should be initialized in your beforeEach or your it, not in the body
        # of define... that's bad. Every new `it` test could mutate this and it won't reset
        # in the beforeEach where you had it.
        testTask =
          type: 'TASK'
          title: 'Feed the kitten'
          due: 201120141900
          group: ['TODAY', 'TOMORROW']
    
    
    
    
      it 'should add task upon request', (done) ->
        # tell the spy on `put` to return something distinct
        PouchDBMock.put.andReturn('something special');
    
        # call the method
        promise = Pouch.addTask testTask
    
        # handle the promise
        promise.then (result) ->
          # assert put was called on your mock
          expect(PouchDBMock.put).toHaveBeenCalledWith(testTask)
          # assert the result was what you expected (the return from the spy)
          expect(result).toBe('something special')
          # and you're done... even though this wasn't asynchronous
          done()
    
        # flush all unresolved promises
        $timeout.flush()
    

答案 1 :(得分:1)

问题是,由于pouchdb回调发生在Angular的摘要周期之外,你必须在pouchdb回调函数中调用$rootScope.$apply()

  app.service 'Pouch', ($q, $rootScope) -> db = new PouchDB 'tasks'
    return {
      addTask : (task) ->
      deferred = $q.defer()
      db.put task, (task.title + task.due), (err, res) ->
      deferred.resolve res
      $rootScope.$apply()
      return deferred.promise
    }

答案 2 :(得分:0)

最有可能的问题是PhantomJS,它需要Function.prototype.bind的垫片才能与PouchDB一起正常工作。你可以从https://github.com/es-shims/es5-shim得到这样的垫片。