Angular 单元测试,是什么导致 $rootScope.$watch() 触发?
Angular unit testing, what's causing the $rootScope.$watch() to fire?
这是我的控制器。
(function() {
'use strict';
app.controller('NavController', navController);
function navController($rootScope, $scope, $location, $window, NavFactory) {
$scope.menuConfig = null;
$scope.menuItems = [];
$scope.isActive = isActive;
$scope.toggleBurgerMenu = toggleBurgerMenu;
$scope.toggleDeviceSettings = toggleDeviceSettings;
$rootScope.$watch('dictionary', function(dictionary) {
if (dictionary) {
init();
}
});
function init() {
$scope.menuConfig = $rootScope.config.navigation;
NavFactory.GetMenuItems().then(onGetMenuItems, $scope.onAPIError);
}
function onGetMenuItems(response) {
$scope.moduleConfig = response.moduleConfig;
$scope.menuItems = response.menuItems;
}
}
})();
这是我的测试套件 (karma, jasmine)
描述('NavController function',函数(){
var $rootScope, $scope, $location, $window, $controller, createController, NavFactory, CacheFactory, toastr;
beforeEach(module('mockedDashboard'));
beforeEach(inject(function(_$rootScope_, _$controller_, _$location_, _$window_, _NavFactory_, _toastr_) {
$location = _$location_;
$window = _$window_;
toastr = _toastr_;
$rootScope = _$rootScope_;
$rootScope.dictionary = jsDictionary;
$rootScope.config = jsConfig;
$rootScope.config.contentFolder = '_default';
$scope = _$rootScope_.$new();
$scope.burgerMenuActive = false;
//mock the parent controller function
$scope.navigate = function(path) {
$location.path('/' + path);
};
// end mock
createController = function() {
return _$controller_('NavController', {
'$scope': $scope
});
};
$controller = createController();
}));
// We are using CacheFactory in this project, when running multiple tests on the controller
// we need to destroy the cache for each test as the controller is initialized for each test.
afterEach(inject(function(_CacheFactory_) {
CacheFactory = _CacheFactory_;
CacheFactory.destroy('defaultCache');
}));
describe('init()', function() {
it('should call NavFactory.GetMenuItems and set moduleConfig and menuItems on $scope', inject(function(_$httpBackend_) {
var $httpBackend = _$httpBackend_;
expect($scope.moduleConfig).toBe(undefined);
expect($scope.menuItems.length).toBe(0);
var responseData = [{
"path": "stats",
"url": "./app/modules/dashboard/modules/score/score.index.html",
"icon": "fa fa-fw fa-pie-chart",
"dashboardEnabled": true,
"burgerMenu": true,
"barMenu": false,
"barMenuOrder": -1,
"bottomMenu": true,
"bottomMenuOrder": 3,
"order_sm": 1,
"order_md": 2,
"order_lg": 2
}];
$httpBackend.expectGET('../content/_default/config/modules.json').respond(responseData);
$httpBackend.flush();
expect($scope.moduleConfig).not.toBe(undefined);
expect($scope.menuItems.length > 0).toBe(true);
}));
});});
我原以为必须 运行 一个 $scope.$digest() 才能触发观察者,让 init() 达到 运行,但它 运行一切正常,测试通过。
这不是问题,我只是想了解为什么 运行ning 因为我不明白观察者是如何开火的?是什么导致它 运行?
在我看来,在全局范围内初始化控制器是个坏主意。这是因为,将来,当你想测试一些其他逻辑上独立的控制器时,你仍然会为每个 it()
块初始化每个控制器,我认为没有理由这样做。
至于为什么你的 $watch
被解雇,你正在将 jsDictionary
设置为 $rootScope
并为每个 it()
块初始化它。在您的控制器代码中,对于 $watch
您只是检查该值是否存在,它会像您在前几行中设置的那样存在,因此它调用 init()
块。使用 $watch 的正确方法是:
$rootScope.$watch('dictionary', function(oldVal, newVal){
});
更好的方法是为每个控制器创建一个 describe()
块,将 beforeEach()
内容放入 describe()
以及 it()
块中,与该特定控制器相关的那些。这也将帮助您构建模块化测试用例。
这是我的控制器。
(function() {
'use strict';
app.controller('NavController', navController);
function navController($rootScope, $scope, $location, $window, NavFactory) {
$scope.menuConfig = null;
$scope.menuItems = [];
$scope.isActive = isActive;
$scope.toggleBurgerMenu = toggleBurgerMenu;
$scope.toggleDeviceSettings = toggleDeviceSettings;
$rootScope.$watch('dictionary', function(dictionary) {
if (dictionary) {
init();
}
});
function init() {
$scope.menuConfig = $rootScope.config.navigation;
NavFactory.GetMenuItems().then(onGetMenuItems, $scope.onAPIError);
}
function onGetMenuItems(response) {
$scope.moduleConfig = response.moduleConfig;
$scope.menuItems = response.menuItems;
}
}
})();
这是我的测试套件 (karma, jasmine) 描述('NavController function',函数(){
var $rootScope, $scope, $location, $window, $controller, createController, NavFactory, CacheFactory, toastr;
beforeEach(module('mockedDashboard'));
beforeEach(inject(function(_$rootScope_, _$controller_, _$location_, _$window_, _NavFactory_, _toastr_) {
$location = _$location_;
$window = _$window_;
toastr = _toastr_;
$rootScope = _$rootScope_;
$rootScope.dictionary = jsDictionary;
$rootScope.config = jsConfig;
$rootScope.config.contentFolder = '_default';
$scope = _$rootScope_.$new();
$scope.burgerMenuActive = false;
//mock the parent controller function
$scope.navigate = function(path) {
$location.path('/' + path);
};
// end mock
createController = function() {
return _$controller_('NavController', {
'$scope': $scope
});
};
$controller = createController();
}));
// We are using CacheFactory in this project, when running multiple tests on the controller
// we need to destroy the cache for each test as the controller is initialized for each test.
afterEach(inject(function(_CacheFactory_) {
CacheFactory = _CacheFactory_;
CacheFactory.destroy('defaultCache');
}));
describe('init()', function() {
it('should call NavFactory.GetMenuItems and set moduleConfig and menuItems on $scope', inject(function(_$httpBackend_) {
var $httpBackend = _$httpBackend_;
expect($scope.moduleConfig).toBe(undefined);
expect($scope.menuItems.length).toBe(0);
var responseData = [{
"path": "stats",
"url": "./app/modules/dashboard/modules/score/score.index.html",
"icon": "fa fa-fw fa-pie-chart",
"dashboardEnabled": true,
"burgerMenu": true,
"barMenu": false,
"barMenuOrder": -1,
"bottomMenu": true,
"bottomMenuOrder": 3,
"order_sm": 1,
"order_md": 2,
"order_lg": 2
}];
$httpBackend.expectGET('../content/_default/config/modules.json').respond(responseData);
$httpBackend.flush();
expect($scope.moduleConfig).not.toBe(undefined);
expect($scope.menuItems.length > 0).toBe(true);
}));
});});
我原以为必须 运行 一个 $scope.$digest() 才能触发观察者,让 init() 达到 运行,但它 运行一切正常,测试通过。
这不是问题,我只是想了解为什么 运行ning 因为我不明白观察者是如何开火的?是什么导致它 运行?
在我看来,在全局范围内初始化控制器是个坏主意。这是因为,将来,当你想测试一些其他逻辑上独立的控制器时,你仍然会为每个 it()
块初始化每个控制器,我认为没有理由这样做。
至于为什么你的 $watch
被解雇,你正在将 jsDictionary
设置为 $rootScope
并为每个 it()
块初始化它。在您的控制器代码中,对于 $watch
您只是检查该值是否存在,它会像您在前几行中设置的那样存在,因此它调用 init()
块。使用 $watch 的正确方法是:
$rootScope.$watch('dictionary', function(oldVal, newVal){
});
更好的方法是为每个控制器创建一个 describe()
块,将 beforeEach()
内容放入 describe()
以及 it()
块中,与该特定控制器相关的那些。这也将帮助您构建模块化测试用例。