CTC*_*CTC 7 unit-testing watch angularjs angularjs-scope
我正在努力测试一个监视几个变量的控制器.在我的单元测试中,即使调用scope,我也无法调用$ watch函数的回调.$ digest().看起来这应该很简单,但我没有运气.
这是我在控制器中的内容:
angular.module('app')
.controller('ClassroomsCtrl', function ($scope, Classrooms) {
$scope.subject_list = [];
$scope.$watch('subject_list', function(newValue, oldValue){
if(newValue !== oldValue) {
$scope.classrooms = Classrooms.search(ctrl.functions.search_params());
}
});
});
Run Code Online (Sandbox Code Playgroud)
这是我的单元测试:
angular.module('MockFactories',[]).
factory('Classrooms', function(){
return jasmine.createSpyObj('ClassroomsStub', [
'get','save','query','remove','delete','search', 'subjects', 'add_subject', 'remove_subject', 'set_subjects'
]);
});
describe('Controller: ClassroomsCtrl', function () {
var scope, Classrooms, controllerFactory, ctrl;
function createController() {
return controllerFactory('ClassroomsCtrl', {
$scope: scope,
Classrooms: Classrooms
});
}
// load the controller's module
beforeEach(module('app'));
beforeEach(module('MockFactories'));
beforeEach(inject(function($controller, $rootScope, _Classrooms_ ){
scope = $rootScope.$new();
Classrooms = _Classrooms_;
controllerFactory = $controller;
ctrl = createController();
}));
describe('Scope: classrooms', function(){
beforeEach(function(){
Classrooms.search.reset();
});
it('should call Classrooms.search when scope.subject_list changes', function(){
scope.$digest();
scope.subject_list.push(1);
scope.$digest();
expect(Classrooms.search).toHaveBeenCalled();
});
});
});
Run Code Online (Sandbox Code Playgroud)
我已经尝试替换所有范围.$ digest()调用范围.$ apply()调用.我试过打电话给他们3到4次,但我无法得到$ watch的回调.
有什么想法可以在这里发生什么?
更新: 这是一个更简单的例子,它不涉及模拟,存根或注入工厂.
angular.module('edumatcherApp')
.controller('ClassroomsCtrl', function ($scope) {
$scope.subject_list = [];
$scope.$watch('subject_list', function(newValue, oldValue){
if(newValue !== oldValue) {
console.log('testing');
$scope.test = 'test';
}
});
Run Code Online (Sandbox Code Playgroud)
和单元测试:
it('should set scope.test', function(){
scope.$digest();
scope.subject_list.push(1);
scope.$digest();
expect(scope.test).toBeDefined();
});
Run Code Online (Sandbox Code Playgroud)
这也失败了"预期未定义的定义".并且没有任何内容记录到控制台.
更新2
我注意到了2个更有趣的事情.
似乎有一个问题是,在调用回调时newValue和oldValue是相同的.我将两者记录到控制台,它们都等于[].因此,例如,如果我将$ watch函数更改为如下所示:
$scope.$watch('subject_list', function(newValue, oldValue){
console.log('testing');
$scope.test = 'test';
});
Run Code Online (Sandbox Code Playgroud)测试顺利通过.不确定为什么newValue和oldValue没有正确设置.
如果我将$ watch回调函数更改为命名函数,并检查是否曾调用过命名函数,那么也会失败.例如,我可以将我的控制器更改为:
$scope.update_classrooms = function(newValue, oldValue){
$scope.test = 'testing';
console.log('test');
};
$scope.watch('subject_list', $scope.update_classrooms);
Run Code Online (Sandbox Code Playgroud)并将我的测试更改为:
it('should call update_classrooms', function(){
spyOn(scope,'update_classrooms').andCallThrough();
scope.$digest();
scope.subject_list.push(1);
scope.$digest();
expect(scope.update_classrooms).toHaveBeenCalled();
});
Run Code Online (Sandbox Code Playgroud)
它失败了,"已经调用了预期的间谍update_classrooms".
在这种情况下,update_classrooms肯定会被调用,因为'test'会被记录到控制台.我很困惑.
我刚刚在自己的代码库中遇到了这个问题,答案结果是我需要一个范围.$ digest()在实例化控制器后立即调用.在测试中,您必须在观察变量的每次更改后手动调用范围.$ digest().这包括在构造控制器以记录初始监视值之后.
另外,正如评论中指定的Vitall一样,集合需要$ watchCollection().
在您的简单示例中更改此手表可解决此问题:
$scope.$watch('subject_list', function(newValue, oldValue){
if(newValue !== oldValue) {
console.log('testing');
$scope.test = 'test';
}
});
Run Code Online (Sandbox Code Playgroud)
至:
$scope.$watchCollection('subject_list', function(newValue, oldValue){
if(newValue !== oldValue) {
console.log('testing');
$scope.test = 'test';
}
});
Run Code Online (Sandbox Code Playgroud)
我做了一个jsfiddle来证明它的区别:
http://jsfiddle.net/ydv8k4zy/ - 原始测试失败 - 因使用$ watch而失败,而不是$ watchCollection
http://jsfiddle.net/ydv8k4zy/1/ - 功能版
http://jsfiddle.net/ydv8k4zy/2/ - $ watchCollection在没有初始范围的情况下失败.$ digest.
如果您在第二个失败的项目上使用console.log,您将看到在范围之后使用相同的旧值和新值调用监视.$ digest()(第25行).