单元测试AngularJs茉莉错误:预期的间谍登录已被调用



我正在用AngularJs和Jasmine进行新的测试。我试图测试登录POST $http服务。我不太确定如何做到这一点,但我有我得到一个错误,我不知道为什么。

This is my login.service.js:

(function () {
  'use strict';
  angular
    .module('app')
    .service('loginService', loginService);
  /** @ngInject */
  function loginService($http) {
    var url = 'http://localhost:8080/login';
    var service = {
      login: login
    };
    return service;
    // ////////// //
    function login(user) {
      return $http.post(url, user);
    }
  }
})();

这是我的测试:

describe('login component', function () {
  var loginService;
  var httpBackend;
  var user = {
    username: 'ADMIN',
    password: 'ADMIN'
  };
  beforeEach(module('app'));
  beforeEach(angular.mock.inject(function (_$httpBackend_, _loginService_) {
    loginService = _loginService_;
    httpBackend = _$httpBackend_;
  }));
  it('loginService should be defined', function () {
    expect(loginService).toBeDefined();
  });
  it('loginService.login should be defined', function () {
    expect(loginService.login).toBeDefined();
  });
  describe('We call the Login Service', function () {
    beforeEach(function () {
      spyOn(loginService, "login").and.callThrough();
    });
    it('we call the service', function () {
      loginService.login(user);
    });
    it('we look if we called the  login service', function () {
      expect(loginService.login).toHaveBeenCalled();
    });
    it('loginService login we send the correct parameters', function () {
      expect(loginService.login).toHaveBeenCalledWith('http://localhost:8080/login', 'POST', user);
    });
  });
});

当它运行时,我得到下一个错误:

PhantomJS 2.1.1 (Linux 0.0.0) login component We call the Login Service we look if we called the  login service FAILED
    Expected spy login to have been called.
    .tmp/app/login/login.spec.js:37:50
    loaded@http://localhost:9876/context.js:151:17
PhantomJS 2.1.1 (Linux 0.0.0) login component We call the Login Service loginService login we send the correct parameters FAILED
    Error: <toHaveBeenCalledWith> : Expected a spy, but got Function.
    Usage: expect(<spyObj>).toHaveBeenCalledWith(...arguments) in node_modules/jasmine-core/lib/jasmine-core/jasmine.js (line 3340)
    .tmp/app/login/login.spec.js:41:54
    loaded@http://localhost:9876/context.js:151:17
PhantomJS 2.1.1 (Linux 0.0.0): Executed 6 of 6 (2 FAILED) (0.041 secs / 0.046 secs)
有谁知道我做错了什么吗?

谢谢! !

你的测试完全错了。由于您正在测试loginService,而login是在该服务上定义的方法,因此您不应该模拟方法login

您应该只模拟不属于您正在测试的代码(在本例中为服务)的方法。因此,根据这一点,您应该模拟在$http服务上执行的post调用。

可以这样做:

describe('login component', function () {
    var loginService;
    var $httpBackend;
    var user = {
        username: 'ADMIN',
        password: 'ADMIN'
    };
    beforeEach(module('app'));
    beforeEach(angular.mock.inject(function (_$httpBackend_, _loginService_) {
        loginService = _loginService_;
        $httpBackend = _$httpBackend_;
        $httpBackend.whenPOST('http://localhost:8080/login', user).respond(201, 'SUCCESS');
    }));
    afterEach(function() {
        $httpBackend.verifyNoOutstandingExpectation();
        $httpBackend.verifyNoOutstandingRequest();
    });
    /**
     * Ideally, these should be inside a seperate describe block.
     */
    // it('loginService should be defined', function () {
    //     expect(loginService).toBeDefined();
    // });
    // it('loginService.login should be defined', function () {
    //     expect(loginService.login).toBeDefined();
    // });
    //Like this.
    describe('Initialization', function(){
        it('loginService should be defined', function () {
            expect(loginService).toBeDefined();
        });
        it('loginService.login should be defined', function () {
            expect(loginService.login).toBeDefined();
        });
    });
    describe('function: login', function () {
        /*
         * We should not be spying a method from the service we're trying to test.
         * This is anti-pattern. We should be mocking the post call instead.
         */
        // beforeEach(function () {
        //     spyOn(loginService, "login").and.callThrough();
        // });
        /** 
         * This test is not doing anything. Each it block should have atleast one expect. 
         */
        // it('we call the service', function () {
        //     loginService.login(user);
        // });
        /**
         * This isn't what should be expected here. We should call the method and expect some response.
         * The response will be mocked by us using $httpBackend's expectPOST method. 
         */
        // it('we look if we called the  login service', function () {
        //     expect(loginService.login).toHaveBeenCalled();
        // });
        // it('loginService login we send the correct parameters', function () {
        //     expect(loginService.login).toHaveBeenCalledWith('http://localhost:8080/login', 'POST', user);
        // });
        it('should respond with status 201 and data 'SUCCESS'', function(){
            var response = loginService.login(user);
            $httpBackend.flush();
            response.success(function(res){
                expect(res).toEqual("SUCCESS");
            });
        });
    });
});

现在您会发现我已经注释了您的大部分代码。那是因为所遵循的做法都是错误的。

应该遵循的做法是,您应该为代码的不同部分使用多个describe块。我就是这么做的。请仔细阅读我给出的评论。这将有助于你更好地理解。

希望这对你有帮助!

看看这个本来以为是间谍,结果得到了功能

我认为你的错误是你的间谍所追求的方法实际上是在原型上。您将在错误跟踪中看到Expected a Spy but got a Function。试着监视原型:

beforeEach(function () { spyOn(loginService.prototype, "login").and.callThrough(); });

it('we look if we called the login service', function () { expect(loginService.prototype.login).toHaveBeenCalled(); });

在你的情况下,因为你使用$http服务,你可以断言ngMock中提供的expectPOST(),查看更多angularjs文档,阅读文档中的示例!

最新更新