【问题标题】:Testing async function with jasmine用茉莉花测试异步功能
【发布时间】:2017-08-01 18:00:39
【问题描述】:

我们在使用 Jasmine 测试异步代码时遇到了意外行为。据我们所知,当您使用done 函数时,在执行完成之前不会调用期望。但是,这并没有发生,因为第二个期望失败了,因此 $ctrl.todos 分配从未发生过

不工作测试

it('initializes the data when $onIinit', (done) => {
  const expected = 'some result';
  const response = Promise.resolve(expected);

  spyOn(myService, 'getAll').and.returnValue(response);

  // This method calls myService.getAll
  $ctrl.$onInit();

  expect(myService.getAll).toHaveBeenCalled();
  expect($ctrl.todos).toEqual(false);

  response.then(done);
});

输出:预期未定义等于 false

另一方面,这是可行的:

it('initializes the data when $onIinit', (done) => {
    const expected = 'some result';
    const response = Promise.resolve(expected);

    spyOn(myService, 'getAll').and.returnValue(response);

    // This method calls myService.getAll
    $ctrl.$onInit();

    expect(myService.getAll).toHaveBeenCalled();
    response
      .then(() => expect($ctrl.todos).toBe(expected))
      .then(done);
  });

输出:测试通过

控制器方法:

$ctrl.$onInit = () => {
  myService.getAll().then((data) => {
    $ctrl.todos = data;
  });
};

【问题讨论】:

    标签: javascript angularjs unit-testing asynchronous jasmine


    【解决方案1】:

    我找到了解决方案。我真的不需要另一个then。 done 的规范在调用完成之前不会完成。此外,它应该始终放在预期之后。

    工作代码:

    it('initializes the data when $onIinit', (done) => {
      const expected = 'some result';
      const response = Promise.resolve(expected);
      spyOn(myService, 'getAll').and.returnValue(response);
      $ctrl.$onInit();
    
      expect(myService.getAll).toHaveBeenCalled();
      response
        .then(() => {
          expect($ctrl.todos).toBe(expected);
          done();
      });
    });
    

    【讨论】:

      【解决方案2】:

      您的方法似乎是正确的,并且可能在afterEach 中调用done 将使其起作用。

      afterEach(function(done) {
        done();
      }, 1000);
      

      但是,我建议使用$httpBackend,它是一种适用于使用$http service 的单元测试应用程序的虚假HTTP 后端实现。无论如何,我们都在使用angularjs,对吧?那么,为什么不利用呢?

      使用$httpBackend,我们可以发出请求,然后用模拟数据响应,而无需真正将请求发送到真实服务器。这是一个说明性的例子

      it('initializes the data when $onIinit', () => {
          const mockData = { data: { expected: 'some result' } };
      
          spyOn(myService, 'getAll').and.callThrough();
      
          $httpBackend.expect('POST', '/my-service/url').respond(200, mockData);
      
          // This method calls myService.getAll
          $ctrl.$onInit();
      
          //flush pending request
          $httpBackend.flush();
      
          expect(myService.getAll).toHaveBeenCalled();
          expect($ctrl.todos).toBeDefined();
      });
      

      一些解释,$httpBackend.expect('POST', '/my-service/url'),这里注意'POST'需要匹配myService.getAll中你的服务使用的method,而'/my-service/url'是你的服务在myService.getAll中也使用的url .

      需要调用$httpBackend.flush();,它会释放所有待处理的请求。

      你需要在你的测试中注入$httpBackend,一个简单的方法是

      describe('$httpBackend service in module ngMock', () => {
          let $httpBackend;
      
          beforeEach(inject(['$httpBackend', (_$httpBackend) => {
              $httpBackend = _$httpBackend;
          }]));
      
          it('$httpBackend is defined', () => {
              // here we can use $httpBackend
              expect($httpBackend).toBeDefined();
          });
      });
      

      另外,请注意$httpBackend 是ngMock 模块的一部分。

      更多关于测试 angularjs 代码的信息here

      希望对你有帮助

      【讨论】:

        【解决方案3】:

        Promise 来自 Angular 世界之外 - 您必须等待下一个事件队列滴答中可用的结果 - 脏(几乎脏)黑客是使用 setTimeout

        angular.module('test', [])
          .controller('underTest', function($scope, myService) {
            $scope.$onInit = function() {
              myService.getAll().then(function(data) {
                $scope.todos = data
              })
            }
          })
        
        describe('Controller: somethingChanged', function() {
          var scope, myService
        
          beforeEach(function() {
            module('test')
          })
        
          beforeEach(function() {
            module(function($provide) {
              $provide.value('myService', {
                getAll: function() {}
              })
            })
          })
        
          beforeEach(inject(function($controller, _$rootScope_, _myService_) {
            myService = _myService_
            scope = _$rootScope_.$new()
            $controller('underTest', {
              $scope: scope
            })
          }))
        
          it('initializes the data when $onIinit', function(done) {
            const expected = 'some result'
            const response = Promise.resolve(expected)
        
            spyOn(myService, 'getAll').and.returnValue(response)
        
            scope.$onInit()
        
            expect(myService.getAll).toHaveBeenCalled();
        
            setTimeout(function() {
              expect(scope.todos).toEqual(expected)
              done()
            })
          });
        });
        <script src="https://cdn.jsdelivr.net/jasmine/2.6.1/jasmine.js"></script>
        <script src="https://cdn.jsdelivr.net/jasmine/2.6.1/jasmine-html.js"></script>
        <script src="https://cdn.jsdelivr.net/jasmine/2.6.1/boot.js"></script>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/jasmine/2.6.1/jasmine.css" />
        
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-mocks.js"></script>

        【讨论】:

          猜你喜欢
          • 2020-02-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多