【问题标题】:Unit testing $q promise within Angular service - Karma, JasmineAngular 服务中的单元测试 $q 承诺 - Karma,Jasmine
【发布时间】:2017-08-02 23:56:27
【问题描述】:

我正在尝试使用 Karma 和 Jasmine 对 Angular 服务进行单元测试。我正在制作我已经看过许多关于如何对来自控制器的服务调用进行单元测试的教程,但我希望在服务本身内进行单元测试。 UsersService.findById() 进行异步调用以在 ID 中查找 ID,但我不知道如何编写通过测试。我很接近,但我需要一点帮助。这是 UsersService 中的函数

users.service.js

        Users.findById = function(id){
            var deferred = $q.defer();

            deferred.resolve(userList.find(function(user){
                return user.id == id;
            }));

            return deferred.promise;
        };

这是规格:

users.service.spec.js

describe("Users Factory", function(){
    var UsersService, $q, $rootScope, $provide, deferred, mockedDeferred;

    var userList = [
        { id: 1, name: "Richard Hendricks", email: "richard@piedpiper.com", phone: 4085550011, pokemon: { isPresent: true, name: "eevee"}, icon: { isPresent: false, name: null} },
        { id: 2, name: "Erlich Bachman", email: "erlich@aviato.com", phone: 4155552233, pokemon: { isPresent: true, name: "celebi"}, icon: { isPresent: false, name: null} },
        { id: 3, name: "Gavin Belson", email: "gavin@hooli.com", phone: 9165554455, pokemon: { isPresent: true, name: "snorlax"}, icon: { isPresent: false, name: null} }
    ];

    var singleUser = { id: 2, name: "Erlich Bachman", email: "erlich@aviato.com", phone: 4155552233, pokemon: { isPresent: true, name: "celebi"}, icon: { isPresent: false, name: null} };

    function mockQ(){
        deferred = $q.defer();
        spyOn(UsersService, "findById");
        return deferred;
    }

    beforeEach(angular.mock.module("testing_app"));
    beforeEach(angular.mock.module(function(_$provide_){
        $provide = _$provide_;
    }));

    beforeEach(inject(function(_UsersService_, _$q_, _$rootScope_){
        UsersService = _UsersService_;
        $q = _$q_;
        $rootScope = _$rootScope_;
    }));

    describe("Users.findById()", function(){

        beforeEach(function(){
            mockedDeferred = mockQ();
        });

        it("should exist", function(){
            expect(UsersService.findById).toBeDefined();
        });

        it("should equal singleUser", function(){
            UsersService.findById(2);
            deferred.resolve(userList.find(function(user){
                return user.id == 2;
            }));
            expect(UsersService.findById).toHaveBeenCalled();
            expect(deferred.promise).toEqual(singleUser);
        });
    });
});

这是我在控制台中的内容:

我很接近,但我如何获得等于 singleUser 变量的承诺?

【问题讨论】:

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


【解决方案1】:

您正在测试 UsersService 服务。因此,您的测试用例应该使用该服务并验证它的行为是否正确。考虑到这一点,检查 findById() 是否在调用它的同一函数(测试)上被调用不提供任何验证,并且是多余和不必要的。在这个特定的测试用例中,更有效的检查将是验证该方法是否返回一个类似 Promise 的对象(有关更多信息,请参阅here)。另一个有效的检查将是验证已知输入的方法的响应值(一旦承诺已经解决)。对于后者,您需要做的是调用$rootScope.$apply();,然后再检查promise 的解析值与预期值(有关示例,请参见angularjs 文档中的testing section on promise)。一旦我们应用此建议,findById() 的测试将如下所示:

describe("Users.findById()", function(){

    it("should exist", function(){
        expect(UsersService.findById).toBeDefined();
    });

    it("should produce a promise", function(){
        var p = UsersService.findById(2);

        expect(p instanceof $q.resolve().constructor).toBeTruthy();
    });

    it("should equal singleUser", function(){
        var p = UsersService.findById(2), resolvedValue;

        p.then(function(value) { resolvedValue = value; });

        $rootScope.$apply();

        expect(resolvedValue).toEqual(singleUser);
    });
});

在某些情况下,被测服务更复杂,并封装了其他低级服务。在这些情况下,您需要监视或模拟那些较低级别的服务,以通过检查它是否调用较低级别的服务来验证被测较高级别的服务是否按预期运行。

【讨论】:

  • 谢谢@yeiniel。我已经稍微编辑了您提供的代码,使其与我的问题的解决方案相匹配,但是当您完成所有繁重的工作时,我给予您信任。一个问题:你能用 $q.resolve().constructor 稍微解释一下构造函数吗?
  • $q.resolve().constructor 指向用于创建 $q 承诺的构造函数,$q.defer().promise 是使用该构造函数创建的实例。这就是为什么我检查使用实例。我链接的 SO 答案中建议的方式是检查变量是否是 $q 承诺。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-03
  • 2014-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多