我正在使用angular@1.5.8
并在路由级别为我的某个组件解析某些资源。该组件按预期工作,但我的测试现在失败了。
PhantomJS 2.1.1 (Linux 0.0.0) module ag.expense-claim ExpenseClaimController: should load data FAILED
TypeError: undefined is not an object (evaluating 'vm.attachedReceipts.results') (line 16)
ExpenseClaimViewController@app/expenses/expense_claim.js:16:50
ExpenseClaimViewController@[native code]
instantiate@node_modules/angular/angular.js:4733:61
$controller@node_modules/angular/angular.js:10369:39
node_modules/angular-mocks/angular-mocks.js:2221:21
$componentController@node_modules/angular-mocks/angular-mocks.js:2264:25
test/expenseSpec.js:18:40
invoke@node_modules/angular/angular.js:4718:24
workFn@node_modules/angular-mocks/angular-mocks.js:3085:26
loaded@http://localhost:9876/context.js:151:17
inject@node_modules/angular-mocks/angular-mocks.js:3051:28
test/expenseSpec.js:14:26
test/expenseSpec.js:11:13
global code@test/expenseSpec.js:1:9
Error: No pending request to flush ! in node_modules/angular-mocks/angular-mocks.js (line 1799)
flush@node_modules/angular-mocks/angular-mocks.js:1799:76
test/expenseSpec.js:53:31
loaded@http://localhost:9876/context.js:151:17
PhantomJS 2.1.1 (Linux 0.0.0): Executed 43 of 43 (1 FAILED) (0.257 secs / 0.387 secs)
error Command failed with exit code 1.
describe('ExpenseClaimController:', function () {
var $scope, ctrl, attachedReceipts;
beforeEach(inject(function ($rootScope, $componentController, $stateParams) {
var attachedReceipts = {results: [{}]};
$scope = $rootScope.$new();
$stateParams.expenseClaimId = 1;
ctrl = $componentController('expenseClaim', {
$scope: $scope,
attachedReceipts: attachedReceipts
});
}));
it('should load data', function () {
…
});
angular.module('ag.expenses')
.component('expenseClaim', {
templateUrl: '…',
controller: ExpenseClaimViewController,
controllerAs: 'vm',
bindings: {
attachedReceipts: "<"
}
});
function ExpenseClaimViewController($stateParams, $uibModal, API, gettextCatalog, alert) {
var vm = this;
vm.attachedReceipts = vm.attachedReceipts.results;
…
}
.state('expense-claim', {
url: '/home_expense_report/:expenseClaimId',
template: '<expense-claim attached-receipts="$resolve.attachedReceipts"></expense-claim>',
resolve: {
attachedReceipts: function (API, $stateParams) {
return API.TransportCostAttachment.query({expenseClaimId: $stateParams.expenseClaimId}).$promise;
}
}
})
我基于How can I mock ui-router's resolve values when testing a state's configuration?实施我的解决方案,但仍无法使其正常运行。我错过了什么?
答案 0 :(得分:1)
角度1.5的分辨率不是DI不像角度2, 因此,对于1.5,你应该将它作为绑定传递,如下所示:
var bindings = { attachedReceipts: attachedReceipts };
ctrl = $componentController('expenseClaim', {
$scope: $scope
}, bindings);
答案 1 :(得分:1)
除了Ameer的回复之外,我还想提供更多细节,因为我很难找到它们,如果你有一个组件,你需要直接对控制器进行单元测试,或者你想测试模板,你有两种方法来创建具有绑定的控制器(无论它们来自ui-router的解析与否都无关紧要)
- 对组件控制器进行单元测试:
就像Ameer所说,you should pass it as a binding:
beforeEach(inject(function(_$componentController_) {
$componentController = _$componentController_;
}));
it('should expose a `hero` object', function() {
// Here we are passing actual bindings to the component
var bindings = {hero: {name: 'Wolverine'}};
var ctrl = $componentController('heroDetail', null, bindings);
expect(ctrl.hero).toBeDefined();
expect(ctrl.hero.name).toBe('Wolverine');
});
$ onInit方法:
如果您的控制器具有$ onInit()方法,它将不会像您预期的那样自动执行,您需要在创建控制器后手动调用该方法,有关此issue here的详细信息
- 单元测试组件的模板:
当单元测试组件的模板时,您仍然需要实例化控制器,这是你可以做的:
beforeEach(inject(($injector) => {
$rootScope = $injector.get('$rootScope');
$compile = $injector.get('$compile');
scope = $rootScope.$new();
scope.attachedReceipts = {results: [{}]};;
template = $compile('<expense-claim attached-receipts="attachedReceipts"></expense-claim>')(scope);
scope.$apply();
});
it('should test something', () => {
//Example of something you could test
expect(template.find('h3').html()).to.eq('this is our first content page');
});
使用第二种方法,将调用$ onInit()方法,因此您无需手动调用它。有关如何在组件can be found here
中单元测试模板的完整教程