如何正确测试AngularJS控制器功能
How to properly test an AngularJS Controller Function
我们刚刚开始在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,并且对peopleNotesSv的调用不会因为无效参数而引发任何错误。我们在单独的测试中测试这两个回调函数。
我们是否应该测试peopleNotesSv.deleteNote函数是否被调用,以便测试更加明确?现在这个测试的编写方式并没有真正告诉别人deleteClick()函数在后台做了什么,这似乎是不正确的。
问问自己,如果使用TDD开发它,你会怎么做。这几乎与Sam指出的方向一致,但这里有一些例子:
控制器测试
- 开始编写一个期望存在deleteClick的测试
- Expect deleteClick设置加载状态(检查处理是否为true)
- 测试是否将服务注入控制器(peopleNotesSv)
- 检查deleteClick是否调用该服务(如已通过间谍提到的)
- 验证$scope.noteId和其他$scope.params是否存在并已设置
就其与主计长的关系而言。所有的标准,无论是失败还是抛出错误等,都应该在Service.spec中进行测试
服务测试
- 确保deleteNote存在
- 检查如果提供了错误数量的参数(更少或更多)会发生什么
- 做一些阳性测试(比如你的noteId=5)
- 做一些阴性测试
- 确保正确调用回调
等等
测试控制器的有效性没有多大意义,因为你需要为你现有的每个控制器进行测试。通过将服务隔离为一个单独的测试单元并确保它满足所有要求,您可以在不进行测试的情况下使用它。这有点像你从未测试过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");
});
因果报应现在吐出这样的东西:
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-您还有一套针对peopleNotesSv服务的测试。
在这种情况下,我会让这个测试保持原样,或者围绕$scope.deleteClick()的特定功能再检查一些事情,比如$scope.processing上是否有任何观察者对.deleteClick(()调用执行任何特定操作。
2-您没有对peopleNotesSv服务的所有可能功能进行任何测试。
在这种情况下,我将编写一个更明确的测试,检查.deleteNote()是否真的执行了它的工作。
在我看来,你应该真正建立测试,尽量不在多个地方测试同一件事,因为这会增加额外的工作,如果你认为,"好吧,当从调用它的特定函数调用它时,我可以测试这个特定的情况。"
如果您想在其他地方将deletNote()作为更大函数的一部分重用,该怎么办?然后,您需要为相同的代码编写另一个测试,因为它是从不同的函数调用的。
因此,我将针对案例1,通过这种方式,您可以为该服务编写所有测试,然后相信这些测试涵盖了该特定测试的其余部分。如果您在错误的输入中抛出错误,或者在实际删除注释时失败,那么您应该信任其他代码来测试它的测试目的。这将大大加快你的测试编写时间,并增加你的测试覆盖所有案例的机会。它还将该服务的所有测试保存在测试代码中的同一位置。
我认为一个很好的问题是,这是一种什么样的测试?单元测试还是端到端测试?
我假设这是一个单元测试,如果是端到端测试,那么你可能需要继续跟踪函数调用,以验证一切是否如你所期望的那样发生。
这里有一些关于单元测试、端到端测试的链接,以及一篇关于两者和Angular的非常好的文章。
- 什么';单元测试、功能测试、验收测试和集成测试之间的区别是什么?(端到端测试也称为集成测试)
- http://www.sitepoint.com/unit-and-e2e-testing-in-angularjs/
- 角度控制器功能赢得'不按指令工作
- 角度控制器功能不;在指令内部调用时,似乎无法始终如一地工作
- 控制器功能在 Angular.js 中不起作用
- 从 AngularJS 中的控制器功能发送数据以供查看
- ng重复调用控制器功能的次数太多
- 无法理解Angular JS中的控制器功能
- 如何使$filter在angularjs中的所有控制器功能中可用
- Ionic Angular/为什么在控制器功能结束之前不调用服务
- 创建通用控制器功能
- $browser.notifyWhenNoOutoutstanding Requests不考虑控制器功能
- 在 ng 单击不部分调用控制器功能时遇到困难
- 角度控制器功能坏了
- AngularJS :如何从控制器功能切换视图
- AngularJS:使用虚拟机从外部调用控制器功能
- 角度控制器功能未运行
- 如何从控制器功能内部获取$scope
- 角度:如何使$scope属性与控制器功能保持同步
- 无法引用控制器功能 ZF2
- AngularJS 控制器功能最佳实践
- 从角度控制器功能访问表单输入