【问题标题】:AngularJS Jasmine 2.0 async testing timed outAngularJS Jasmine 2.0 异步测试超时
【发布时间】:2014-09-23 20:57:48
【问题描述】:

我知道这个问题已经被问过很多次了,我已经查看了其他问题并关注了他们,但我似乎无法解决这个问题。

基本上,我在 Service 中有一个函数可以将数据放入 pouchDB。函数addTask 将返回一个promise,当数据库插入成功时,该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,但是没有用。

【问题讨论】:

    标签: angularjs coffeescript jasmine phantomjs pouchdb


    【解决方案1】:

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

    1. 要么:您似乎没有单独测试您的 Pouch 服务。那个异步调用是怎么回事?它在做什么?有什么可以模拟和注入的东西,以便使用$q.when()$timeout.$flush() 进行测试?
    2. ...或...您的意思是这是一个集成测试,在这种情况下,您可以使用beforeEachafterEachjasmine. DEFAULT_TIMEOUT_INTERVAL 设置为该测试的更高数字

    编辑:您在帖子中所写的服务和接受的答案不能单独测试。

    如果你想单独测试它,你需要将 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()
    

    【讨论】:

    • 不,这不是集成测试。我不明白你所说的不孤立的意思,因为我已经嘲笑了应用程序和服务。我尝试将承诺包装到$q.when(promise).then 并添加$timeout.flush,它没有工作
    • 您肯定在进行集成测试。因为您没有注入PouchDB,而是在您的服务工厂中对其进行了更新。换句话说,你没有注入你的依赖。因此,您不是在孤立地进行测试,因为您无法按照编写代码的方式进行测试。每个测试调用实际上调用 PouchDB,因此,也会测试 localStorage 或 HTTP,这取决于您使用 pouch 做什么。
    【解决方案2】:

    问题在于,由于 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
        }
    

    【讨论】:

    • 你想要运行 $digest 的代码的主体应该由应用程序按预期包装。这会将代码包装在 Angular 中适当的异步错误处理中。并且测试仍然没有被隔离。请参阅我编辑的答案。
    【解决方案3】:

    问题很可能出在 PhantomJS 上,它需要Function.prototype.bind 的垫片才能与 PouchDB 正常工作。您可以从https://github.com/es-shims/es5-shim 获得这样的垫片。

    【讨论】:

    • 我已经使用了 es5-shim,如果没有,错误信息会有所不同(即文件甚至无法编译)在 Chrome 中运行 karma 时也会失败。
    猜你喜欢
    • 2013-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-03
    • 1970-01-01
    相关资源
    最近更新 更多