我对测试很陌生,而且我一直在努力为具有服务依赖性的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);
),即使我的控制器现在坏了,我的测试仍然通过。但是,一旦我在控制器中更改了方法名称,以便修复更改服务的方法名称导致的实际破损,我的测试因为尝试调用旧的addExcitement
方法而中断。
这表明我需要通过将茉莉花间谍对象行更改为MockStringService = jasmine.createSpyObj("StringService", ["addExclamations"]);
来手动保持方法名称与服务同步。
所有这些似乎都向后退了,因为当我更改服务的方法名称而不改变控制器引用该服务名称的方式时,我觉得我的测试应该会中断。但是我不确定如何在这里获得两全其美,因为如果我希望我的测试能够以某种方式跟踪该服务名称,那么当它没有办法再次通过时我更改了服务和控制器中的方法名称,因为spyObj仍然具有旧名称。
对此背后的策略的任何见解或建议将不胜感激。我将向一些学生讲授这个,我主要是努力确保我遵循最佳实践。
答案 0 :(得分: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来获取服务实例,你可以实际注入你的服务。我不记得为什么我这样做了。 :)