AngularJS - 使用$ http服务的测试服务方法

时间:2012-08-23 03:38:09

标签: unit-testing angularjs

我的服务功能看起来像这样:

addStatement: function(user, action, object) {
            var statement = {
                    user: user,
                    action: action,
                    object: object
            };
            $http({
                method: 'POST', 
                url: '/foo/bar', 
                data: statement, 
                headers: {Authorization: 'Basic YWxhZGRpbjpvcGVuIHNlc2FtZQ=='}
            }).success(function(data) {
                alert("success: " + data);
            });
        }

我想为这个方法编写单元测试,但我不确定如何使其工作。基本上,我想测试发送的数据是否已从函数参数中正确构造,并且发送了正确的Authorization标头。

我一直在阅读如何使用$httpBackend,但是那里的例子是测试一个控制器,只是在发出请求并返回时在$ scope上发生了变化。有没有办法实现我想要的测试?或者我说错了什么?

2 个答案:

答案 0 :(得分:37)

测试服务与测试控制器没有太大差别,因此原理是相同的。基本上你需要:

  • 注入测试中的对象
  • 设置模拟(如果有的话) - 在这里你使用$ httpBackend mock
  • 在正确的轨道上
  • 在您测试的对象上运行方法并验证结果

如果您在测试服务方法导致$ http POST调用之后,您可以像这样编写测试(省略非必要部分):

beforeEach(module('MyApp'));
beforeEach(inject(function(MyService, _$httpBackend_) {
    service = MyService;
    $httpBackend = _$httpBackend_; // angular strips the underscores so
                                   // we don't need to use unique names
    // https://docs.angularjs.org/api/ngMock/function/angular.mock.inject
}));

it('should invoke service with right paramaeters', function() {
    $httpBackend.expectPOST('/foo/bar', {
        "user": "testUser",
        "action": "testAction",
        "object": {}
    }, function(headers){
        return headers.Authorization === 'Basic YWxhZGRpbjpvcGVuIHNlc2FtZQ==';
    }).respond({});
    service.addStatement('testUser', 'testAction', {});
    $httpBackend.flush();
});

以下是工作中的jsFiddle,说明了此测试的实际应用:http://jsfiddle.net/pkozlowski_opensource/MkrjZ/2/

最后一句话:最好不要在单元测试中使用.alert(),因为这些警报会暂停执行测试。

答案 1 :(得分:2)

我写了一篇关于这个主题的博客文章,展示了使用angular的内置模拟和编写自己的模拟,可能有助于您更深入地理解:

How to mock AngularJS modules and inject them in your testacular tests?

简而言之,它定义了第二个可以模拟所需行为的测试模块:

var apptastic      = angular.module('apptastic', []),
    apptasticMock  = angular.module('apptasticMock', []);

并覆盖原始行为,例如像这样:

apptasticMock.service("socket", function($rootScope){
... // overwrite
});

然后它必须在测试中加载 - 这是有效的,因为角度按照它们加载的顺序覆盖已定义的服务:

describe("Socket Service", function(){
  var socket;

  beforeEach(function(){
    module('apptastic');
    module('apptasticMock');

    inject(function($injector) {
      socket = $injector.get('socket');
    });
  });

  it("tests something", function(){
  ... // test
  });

});

使用良好的代码结构,即使继承也不是真正的问题,因为能够对应该被模拟的内容进行细粒度控制,以及应该从原始代码中使用什么。