如何在Jasmine + Karma单元测试期间调用我的Angular服务中的$ http请求?

时间:2016-11-30 23:34:50

标签: javascript angularjs unit-testing karma-runner karma-jasmine

所以,我目前正在使用Jasmine + Karma在AngularJS中测试一个函数。我正在尝试创建一个对象,看看它是否已成功添加到数据库中。这个函数调用另一个异步函数,我在测试中似乎无法解释。我知道它永远不会被召唤,因为我没有收到任何回复。没有控制台日志,错误或任何东西。截至目前,我有一个名为StoringService的服务,如下所示:

myApp.service("StoringService", function($http){

    // Stores my object
    this.store = function(object,success,error){
        var request = {
            method: "post",
            url: "example.com",
            headers: {/*...*/},
            data: object.toJSON()
        };
        $http(request).then(
            function(response){
                console.log("Success! Yay!");
                success(response.data.url.script);
            },function(response){
                console.log("Error! Bummer...");
                error(response);
            }
        );
    };
});

我正在测试的主控制器是我的CreationController。这有一个名为Submit的函数,它调用上述服务。这在我运行我的应用程序时有效,但在我运行测试时似乎没有。 submit方法(在CreationController中)看起来像这样:

myApp.controller("CreationController", ["$scope","StoringService",
    function($scope,StoringService){

        $scope.newObject = new Object();
        $scope.successAlert = false; // changed by success() in StoringService
        $scope.errorAlert = false; // changed by error() in StoringService

        $scope.submit = function(){
            StoringService.store($scope.newObject,
                function(message){
                    console.log(message);
                },
                function(error){
                    console.log(error);
                }
            );
        }
}]);

最后,当我进入Jasmine + Karma测试时,我注意到我的对象没有被创建,并且经过进一步检查,我的StoringService工作正常,直到实际的$ http请求(示例代码中的第11行)。我不完全确定如何触发这个动作。这是我目前的测试结果。

//creation_test.js
var scope, service;

describe('Creation Test', function(){
    originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
    jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;

    beforeEach(module('MyApplication'));
    beforeEach(inject($rootScope, $controller, $injector){

        scope = $rootScope.$new();
        service = $injector.get('StoringService');

        $controller('CreationController',{
            $scope: scope,
            StoringService: service
        });
    }));

    // Fails but should pass
    it('should create an object',function(done){
        scope.newObject.name = "new_object";
        scope.newObject.data = "object data";
        scope.submit();
        setTimeout(function(){
            expect(scope.successAlert).toBe(true);
            expect(scope.errorAlert).toBe(false);
            done();
        },5000);
    });

    afterEach(function(){
        jasmine.DEFAULT_TIMEOUT_INTERVL = originalTimeout;
    });
);

我不完全确定为什么这不起作用或如何解决它。 successAlert和errorAlert的值永远不会更改,这使我相信永远不会调用$ http请求。我一直在尝试here的可能解决方案,但到目前为止,没有人解决过这个问题。我非常感谢帮助解决这个问题,如果有人找到解决方案,我会非常高兴。

2 个答案:

答案 0 :(得分:0)

这是一个工作示例。测试后确保使用args调用服务方法。

expect(loginService.authenticate).toHaveBeenCalledWith({
          user: 'david@yahoo.com',
          pass: '123'
        });

https://plnkr.co/edit/6xmCPm66QWRZyfo1v0h5?p=preview

答案 1 :(得分:0)

我已经调整了你的测试。在进行单元测试时,我们不想花时间调用后端,这就是我们需要模拟HTTP调用的原因。当您的store方法返回一个promise时,我们会在单元测试中模拟这种行为,从而创建一个我们可以控制的延迟保证。稍后您可以resolvereject承诺测试成功和错误。我希望它有所帮助。

describe('Controller: CreationController', function() {

  beforeEach(module('MyApplication'));

  var storingService;
  var deferred;
  // Mock services and spy on methods
  beforeEach(inject(function($q, StoringService) {
    deferred = $q.defer();
    storingService = StoringService;
    spyOn(storingService, 'store').and.returnValue(deferred.promise);
  }));

  var CreationController;
  var scope;
  // Initialize the controller and a mock scope
  beforeEach(inject($rootScope, $controller) {
    scope = $rootScope.$new();
    CreationController = $controller('CreationController', {
      $scope: scope,
      StoringService: storingService
    });
  }));

  it('should create an object', function() {
    scope.submit();
    scope.$apply(function() {
      deferred.resolve();
    });
    expect(scope.successAlert).toBe(true);
    expect(scope.errorAlert).toBe(false);
  });

);