我们刚开始在AngularJS项目中实施茉莉花测试,我有一个问题:
我们想测试这个控制器功能:
$scope.deleteClick = function () {
$scope.processing = true;
peopleNotesSrv.deleteNote($scope.currentOperator.operatorId, $scope.noteId, $scope.deleteSuccessCallback, $scope.deleteErrorCallback);
};
我们写了以下测试:
it('deleteClick should pass proper parameters to peopleNoteSrv', function () {
$controllerConstructor('PeopleNoteEditCtrl', { $scope: $scope });
$scope.noteId = 5;
expect(function () { $scope.deleteClick(); }).not.toThrow();
});
此测试确保当我们调用$ scope.deleteClick()函数时,$ scope.processing设置为true,并且对peopleNotesSrv的调用不会因为参数无效而抛出任何错误。我们在单独的测试中测试两个回调函数。
我们是否应该测试是否调用了peopleNotesSrv.deleteNote函数,以便测试更明确?现在编写这个测试的方式并没有真正告诉别人deleteClick()函数在幕后做了什么,这似乎是不正确的。
答案 0 :(得分:1)
问问自己,如果你使用TDD开发了它,你会做些什么。这几乎是Sam指出的方向,但这里有一些例子:
这与控制器有关。应该在Service.spec中测试所有标准是失败还是抛出错误等。由于我在这里不详细了解您的服务,所以有一些例子
......等等。
在控制器中测试有效性并不是很有意义,因为您需要为每个控制器执行此操作。通过将服务隔离为一个单独的测试单元并确保它满足所有要求,您可以在不进行测试的情况下使用它。它有点像你从未测试jQuery功能或Angular jQLite一样,因为你只是期望它们做他们应该做的事情:)
编辑:
很容易让我们举个例子。首先,我们创建服务测试,以确保在没有提供适当数量的参数的情况下调用失败:
describe('Service: peopleNoteSrv', function () {
// load the service's module
beforeEach(module('angularControllerServicecallApp'));
// instantiate service
var peopleNoteSrv;
beforeEach(inject(function (_peopleNoteSrv_) {
peopleNoteSrv = _peopleNoteSrv_;
}));
it('should throw error on false number of arguments', function () {
expect(function() { peopleNoteSrv.deleteNote('justOneParameter'); }).toThrow();
});
});
现在确保测试通过让我们在服务方法中创建错误抛出部分
angular.module('angularControllerServicecallApp')
.service('peopleNoteSrv', function peopleNoteSrv() {
this.deleteNote = function(param1, param2, param3) {
if(arguments.length !== 3)
throw Error('Invalid number of arguments supplied');
return "OK";
};
});
现在让我们创建2个演示控制器,FirstCtrl将正确执行,但SecondCtrl应该失败
angular.module('angularControllerServicecallApp')
.controller('FirstCtrl', function ($scope, peopleNoteSrv) {
$scope.doIt = function() {
return peopleNoteSrv.deleteNote('param1', 'param2', 'param3');
}
});
angular.module('angularControllerServicecallApp')
.controller('SecondCtrl', function ($scope, peopleNoteSrv) {
$scope.doIt = function() {
return peopleNoteSrv.deleteNote('onlyOneParameter');
}
});
作为演示的两个控制器都有以下测试:
it('should call Service properly', function () {
expect(scope.doIt()).toBe("OK");
});
Karma现在吐出这样的东西:
Error: Invalid number of arguments supplied
at [PATH]/app/scripts/services/peoplenotesrv.js:15
at [PATH]/app/scripts/controllers/second.js:13
at [PATH]/test/spec/controllers/second.js:20
因此,您确切地知道您错过了更新SecondCtrl。当然,这应该适用于任何使用Service方法的测试。
希望这就是你的意思。
答案 1 :(得分:0)
我认为答案取决于它。
有两种情况:
1 - 您还有一套针对peopleNotesSrv服务的测试。
在这种情况下,我会按原样保留此测试,或者检查$ scope.deleteClick()的特定功能周围的一些内容,例如,如果$ scope.processing上有任何观察者做任何特定的关于a .deleteClick()调用。
2 - 您没有对peopleNotesSrv服务的所有可能功能进行任何测试。
在这种情况下,我会编写一个更明确的测试来检查.deleteNote()是否实际执行了它的工作。
在我看来,你应该真正构建测试并尝试不在多个地方测试相同的东西,因为这增加了额外的工作,并且如果你认为,可能会在测试中产生漏洞,“我可以测试这个特定的从调用它的特定函数调用它的情况。“
如果你想在不同的地方重用deletNote()作为更大函数的一部分怎么办?那么你需要为同一个代码编写另一个测试,因为它是从不同的函数调用的。
所以我的目标是案例1,这样你就可以为该服务编写所有测试,然后相信这些测试涵盖了这个特定测试的其余部分。如果您在输入错误或未能实际删除注释时抛出错误,您应该相信其他代码可以测试它的设计测试内容。这将大大加快您的测试编写时间并增加测试涵盖所有案例的机会。它还将该服务的所有测试保存在测试代码中的相同位置。
我认为开始时还有一个很好的问题是这是一种什么样的测试?单元测试或端到端测试?
我假设它是我的答案的单元测试,如果它是一个端到端的测试,那么你可能想继续跟随函数调用来验证一切正如你所期望的那样。
以下是有关单元测试,端到端测试的一些链接,以及关于Angular和Angular的一篇非常好的文章。