【问题标题】:AngularJS - Testing service method that uses $http serviceAngularJS - 使用 $http 服务的测试服务方法
【发布时间】:2012-08-18 13:01:39
【问题描述】:

我的服务上有一个看起来像这样的函数:

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

我想为这个方法编写一个单元测试,但我不知道如何使它工作。基本上,我想测试发送的数据是从函数参数正确构造的,并且发送了正确的授权头。

我一直在阅读如何使用$httpBackend,但是那里的示例是测试控制器,并且只是在发出和返回请求时在 $scope 上更改的内容。有没有办法实现我想要的测试?还是我出了什么问题?

【问题讨论】:

    标签: unit-testing angularjs


    【解决方案1】:

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

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

    如果您在测试服务方法导致 $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 避免alerts。另一种可能性是您可能想要注入 $window 并使用 $window.alert 以便您可以模拟它以进行测试。
    • 学习时刻:如果您取出$httpBackend.flush();,您的测试将不会像预期的那样。
    【解决方案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
    });
    

    然后必须在测试中加载它 - 这是因为 Angular 会按照加载顺序覆盖已定义的服务:

    describe("Socket Service", function(){
      var socket;
    
      beforeEach(function(){
        module('apptastic');
        module('apptasticMock');
    
        inject(function($injector) {
          socket = $injector.get('socket');
        });
      });
    
      it("tests something", function(){
      ... // test
      });
    
    });
    

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

    【讨论】:

      猜你喜欢
      • 2013-03-29
      • 2017-12-03
      • 2013-06-08
      • 1970-01-01
      • 2015-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-20
      相关资源
      最近更新 更多