简体   繁体   English

使用jasmine测试异步功能

[英]Testing async function with jasmine

We are facing an unexpected behavior while testing async code with Jasmine. 我们在使用Jasmine测试异步代码时遇到了意外行为。 As far as we know, when you are using the done function, expectations are not called until done is executed. 据我们所知,当您使用done函数时,在执行done之前不会调用期望值。 But, that's not happening because the second expectation is failing, hence the $ctrl.todos assignment never happened 但是,这并没有发生,因为第二个期望是失败的,因此$ctrl.todos任务从未发生过

Not working test 不工作测试

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);
});

Output: Expected undefined to equal false 输出:预期未定义为等于false

On the other hand, this is working: 另一方面,这是有效的:

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);
  });

Output: test pass 输出:测试通过

Controller method: 控制器方法:

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

I found a solution. 我找到了解决方案。 I don't really need another then . then我真的不需要另一个。 Specs with done will not complete until it's done is called. done规范在done调用之前不会完成。 Also, it should be always placed after expectations. 此外,它应始终放在预期之后。

Working code: 工作代码:

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();
  });
});

Your aproach seems to be correct, and probably calling done within afterEach will make it works. 您的形式给出了似乎是正确的,而且很可能调用doneafterEach会使它的工作原理。

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

But, I would recommend using $httpBackend , the fake HTTP backend implementation suitable for unit testing applications that use the $http service . 但是,我建议使用$ httpBackend ,这是一种适用于使用$ http服务的单元测试应用程序的虚假HTTP后端实现。 We are using angularjs anyway, right? 无论如何我们正在使用angularjs ,对吗? So, why not take advantage of? 那么,为什么不利用?

With $httpBackend we can make the requests, then response with mock data without really sending the request to a real server. 使用$ httpBackend,我们可以发出请求,然后使用模拟数据进行响应,而无需将请求真正发送到真实服务器。 Here an ilustrative example 这是一个有说服力的例子

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();
});

Some explanations, $httpBackend.expect('POST', '/my-service/url') , here note that 'POST' need to match the method used by your service in myService.getAll , and '/my-service/url' is the url also used by your service in myService.getAll . 一些解释, $httpBackend.expect('POST', '/my-service/url') ,请注意'POST'需要匹配myService.getAll服务使用的method ,以及'/my-service/url'是您的服务在myService.getAll也使用的url

It is required to call $httpBackend.flush(); 需要调用$httpBackend.flush(); , it will release all pending requests. ,它将释放所有待处理的请求。

You will need to inject $httpBackend into your tests, an easy way would be 您需要在测试中注入$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();
    });
});

Also, note that $httpBackend is part of the ngMock module. 另请注意, $httpBackendngMock模块的一部分。

More info about testing angularjs code here 有关测试angularjs代码的更多信息,请点击此处

Hope it helps 希望能帮助到你

Promise is from outside Angular world - you have to wait for result that will be available in next event queue tick - dirty (almost dirty) hack is to use setTimeout Promise来自Angular世界之外 - 你必须等待下一个事件队列中可用的结果 - 脏(几乎是脏的)hack是使用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> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM