【问题标题】:AngularJS Unit Testing Controller w/ service dependency in JasmineJasmine 中带有服务依赖项的 AngularJS 单元测试控制器
【发布时间】:2016-05-03 17:01:52
【问题描述】:

我是测试的新手,我一直在尝试找到对具有服务依赖关系的 AngularJS 控制器进行单元测试的最佳策略。这是源代码:

app.service("StringService", function() {
    this.addExcitement = function (str) {
        return str + "!!!";
    };
});

app.controller("TestStrategyController", ["$scope", "StringService", function ($scope, StringService) {
    $scope.addExcitement = function (str) {
        $scope.excitingString = StringService.addExcitement(str);
    };
}]);

还有我目前正在使用的测试:

describe("Test Strategy Controller Suite", function () {
    beforeEach(module("ControllerTest"));

    var $scope, MockStringService;

    beforeEach(inject(function ($rootScope, $controller) {
        $scope = $rootScope.$new();
        MockStringService = jasmine.createSpyObj("StringService", ["addExcitement"]);
        $controller("TestStrategyController", {$scope: $scope, StringService: MockStringService});
    }));

    it("should call the StringService.addExcitement method", function () {
        var boringString = "Sup";
        $scope.addExcitement(boringString);
        expect(MockStringService.addExcitement).toHaveBeenCalled();
    });
});

这个测试通过了,但我对某些事情感到困惑:如果我更改服务中方法的名称(假设我将其称为addExclamations 而不是addExcitement,但不是在控制器中使用它的位置(仍然说$scope.excitingString = StringService.addExcitement(str);),即使我的控制器现在坏了,我的测试仍然通过。但是,一旦我也更改了控制器中的方法名称,以解决因更改服务的方法名称而导致的实际损坏,我的tests 因为它试图调用旧的 addExcitement 方法而中断。

这表明我需要通过将 jasmine spy 对象行更改为 MockStringService = jasmine.createSpyObj("StringService", ["addExclamations"]); 来手动保持方法名称与服务同步。

所有这一切对我来说似乎都是倒退的,因为我觉得当我更改服务的方法名称而不更改控制器引用该服务名称的方式时我的测试应该会中断。但是我不确定如何在这里两全其美,因为如果我希望我的测试以某种方式跟踪该服务名称,那么当我在两者中更改方法名称时,它就无法再次通过服务和控制器,因为 spyObj 仍然使用旧名称。

任何有关这背后的策略的见解或建议将不胜感激。我将向一些学生教授这一点,并且主要是为了确保我在这方面遵循最佳实践。

【问题讨论】:

    标签: angularjs unit-testing jasmine angular-mock


    【解决方案1】:

    我会说这是您的测试代码工作方式的预期结果,仅仅是因为您创建了一个“全新的”模拟服务对象。我想你知道我在说什么。

    我通常做的是获取服务实例并模拟方法,而不是创建一个全新的模拟对象。

       beforeEach(inject(function ($rootScope, $controller, $injector) {
           $scope = $rootScope.$new();
           MockStringService = $injector.get('StringService'); 
           spyOn(MockStringService , 'addExcitement').andReturn('test');
           $controller("TestStrategyController", {$scope: $scope, StringService: MockStringService});
       }));
    

    请注意 andReturn() 是 jasmine 1.x 方法,取决于您使用的版本,您可能需要稍微更改代码。

    这样的话,如果你改变StringService中的方法名,你应该会从spyOn()方法中得到错误,因为该方法不再存在了。

    另一件事是,您不必像我那样使用 $injector 来获取服务实例,实际上您可以直接注入您的服务。我不记得我为什么这样做了。 :)

    【讨论】:

    • 在我看来,关于完全模拟服务(使用 jasmine.createSpyObj() 之类的东西)并引入对实际服务的引用并从中进行间谍活动,我一直在反复讨论。这似乎是支持后者的另一点,因为这非常有效。如果我进行这样的更改,必须更改测试代码仍然很烦人,但我想这只是拥有最新/准确测试的本质。谢谢!
    • 如果您进行这样的更改,您仍然需要更改测试代码,至少对于这一行期望(MockStringService.addExcitement),不是吗?我认为您可以遍历实际服务的所有功能,监视它们,然后返回间谍对象引用,以便稍后设置期望。通过这种方式,您可以实现您想要的,但您最终可能会针对错误的方法进行测试。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多