如何通过父元素测试组件绑定的更改? [英] How to test changes on Component Bindings by parent element?
问题描述
我有一个如下组件,想测试 $ onChange
方法在绑定 myBinding
改变。
I have a component like follows and would like to test what the $onChange
method does in case the binding myBinding
changes.
我整个上午都试过,但找不到办法解决这个问题。
I tried the whole morning, but could not find a way to solve this.
angular
.module('project.myComponent', [])
.component('myComponent', {
bindings: {
myBinding: '<'
},
template: '<div>{{$ctrl.result}}</div>',
controller: myComponentController
});
function myComponentController($filter, someService) {
var ctrl = this;
ctrl.result = 0;
$ctrl.$onChange = function (changes) {
if(angular.isDefined(changes.myBinding)) {
if(angular.isDefined(changes.myBinding.currentValue)) {
if(angular.isDefined(changes.myBinding.currentValue != changes.myBinding.previousValue)) {
myService.doSomething(changes.myBinding.currentValue).then(
function(data) {
ctrl.result = changes.myBinding.currentValue * 3;
}
);
}
}
}
}
}
我希望我的测试表现得像是更改绑定值的组件父级。
I would like my test acting like it is the components parent which changes the value of the binding.
require('angular-mocks');
describe('myComponment', function() {
var element, scope;
beforeEach(inject(function(_$rootScope_, _$compile_) {
}));
fit('should display the controller defined title', function() {
// prepare test and set myBinding to 10
expect(component.result).toBe(30);
});
});
这可能吗?怎么样?
任何提示? Plunker,CodePen或其他示例?
Is that possible? How? Any hints? Plunker, CodePen or other examples?
推荐答案
测试AngularJS组件与测试指令没有多大区别。
Testing AngularJS components doesn't differ much from testing directives.
要测试控制器的方法/属性,可以使用 element.controller(componentName)
方法( componentName
- 是一个camelCase指令/组件名称)。
To test controller's methods / properties, you can access the instance of the component's controller using element.controller("componentName")
method (componentName
- is a camelCase directive / component name).
以下是使用的示例 $ compile
服务测试组件和 $ onChanges
hook:
Here is example using $compile
service to test the component and $onChanges
hook:
angular.module('myApp', [])
.component('myComponent', {
bindings: {
myBinding: '<'
},
template: '<div>{{$ctrl.result}}</div>',
controller: 'myComponentController'
})
.controller('myComponentController', ['$filter', 'myService', function myComponentController($filter, myService) {
var ctrl = this;
ctrl.$onInit = onInit;
ctrl.$onChanges = onChanges;
function onInit() {
ctrl.result = ctrl.myBinding;
}
function onChanges(changes) {
if (angular.isDefined(changes.myBinding)) {
if (angular.isDefined(changes.myBinding.currentValue)) {
if (!angular.equals(changes.myBinding.currentValue, changes.myBinding.previousValue)) {
myService.doSomething(changes.myBinding.currentValue).then(
function (data) {
ctrl.result = data;
}
);
}
}
}
}
}])
.service('myService', ['$timeout', function ($timeout) {
return {
doSomething: function (x) {
return $timeout(function () {
return x * 3;
}, 500);
}
};
}]);
/*
TEST GO HERE
*/
describe('Testing a component controller', function() {
var $scope, ctrl, $timeout, myService;
beforeEach(module('myApp', function ($provide) {
}));
beforeEach(inject(function ($injector) {
myService = $injector.get('myService');
$timeout = $injector.get('$timeout');
}));
describe('with $compile', function () {
var element;
var scope;
var controller;
beforeEach(inject(function ($rootScope, $compile) {
scope = $rootScope.$new();
scope.myBinding = 10;
element = angular.element('<my-component my-binding="myBinding"></my-component>');
element = $compile(element)(scope);
controller = element.controller('myComponent');
scope.$apply();
}));
it('should render template', function () {
expect(element[0].innerText).toBe('10'); //initial
$timeout.flush(); //onchanges happened and promise resolved from the service
//undefined -> 10
expect(element[0].innerText).toBe('30');
});
it('should reflect to changes', function () {
spyOn(myService, "doSomething").and.callThrough();
scope.myBinding = 15; //change the binding
scope.$apply(); //we need to call $apply to pass the changes down to the component
$timeout.flush();
expect(myService.doSomething).toHaveBeenCalled(); // check if service method was called
expect(controller.result).toBe(45); // check controller's result value
});
})
});
.as-console-wrapper {
height:0;
}
<!DOCTYPE html>
<html>
<head>
<!-- jasmine -->
<script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine.js"></script>
<!-- jasmine's html reporting code and css -->
<script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine-html.js"></script>
<link href="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine.css" rel="stylesheet" />
<script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/boot.js"></script>
<!-- angular itself -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.js"></script>
<!-- angular's testing helpers -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-mocks.js"></script>
</head>
<body>
<!-- bootstrap jasmine! -->
<script>
var jasmineEnv = jasmine.getEnv();
// Tell it to add an Html Reporter
// this will add detailed HTML-formatted results
// for each spec ran.
jasmineEnv.addReporter(new jasmine.HtmlReporter());
// Execute the tests!
jasmineEnv.execute();
</script>
</body>
</html>
你也可以使用 $ componentController
测试您的组件服务。但在这种情况下,您需要在测试中显式调用生命周期钩子,例如:
You can also test your components using $componentController
service. But in this case you will need to explicitly call life-cycle hooks in your tests, like:
ctrl = $componentController('myComponent', {$scope: scope}, { myBinding: 10 });
ctrl.$onInit();
要测试 $ onChanges
hook,你会需要传递一个正确构造的更改对象作为参数:
To test $onChanges
hook, you will need to pass a "properly" constructed changes object as argument:
angular.module('myApp', [])
.component('myComponent', {
bindings: {
myBinding: '<'
},
template: '<div>{{$ctrl.result}}</div>',
controller: 'myComponentController'
})
.controller('myComponentController', ['$filter', 'myService', function myComponentController($filter, myService) {
var ctrl = this;
ctrl.$onInit = onInit;
ctrl.$onChanges = onChanges;
function onInit() {
ctrl.result = ctrl.myBinding;
}
function onChanges(changes) {
if (angular.isDefined(changes.myBinding)) {
if (angular.isDefined(changes.myBinding.currentValue)) {
if (!angular.equals(changes.myBinding.currentValue, changes.myBinding.previousValue)) {
myService.doSomething(changes.myBinding.currentValue).then(
function (data) {
ctrl.result = data;
}
);
}
}
}
}
}])
.service('myService', ['$timeout', function ($timeout) {
return {
doSomething: function (x) {
return $timeout(function () {
return x * 3;
}, 500);
}
};
}]);
/*
TEST GO HERE
*/
describe('Testing a component controller', function () {
var $scope, ctrl, $timeout, myService;
beforeEach(module('myApp', function ($provide) {
}));
beforeEach(inject(function ($injector) {
myService = $injector.get('myService');
$timeout = $injector.get('$timeout');
}));
describe('with $componentController', function () {
var scope;
var controller;
beforeEach(inject(function ($rootScope, $componentController) {
scope = $rootScope.$new();
scope.myBinding = 10;
controller = $componentController('myComponent', {$scope: scope}, {myBinding: 10});
controller.$onInit();
}));
it('should reflect to changes', function () {
spyOn(myService, "doSomething").and.callThrough();
controller.$onChanges({myBinding: {currentValue: 15, previousValue: 10}});
$timeout.flush(); // resolve service promise
expect(myService.doSomething).toHaveBeenCalled(); // check if service method was called
expect(controller.result).toBe(45); // check controller's result value
});
})
});
.as-console-wrapper {
height:0;
}
<!DOCTYPE html>
<html>
<head>
<!-- jasmine -->
<script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine.js"></script>
<!-- jasmine's html reporting code and css -->
<script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine-html.js"></script>
<link href="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine.css" rel="stylesheet" />
<script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/boot.js"></script>
<!-- angular itself -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.js"></script>
<!-- angular's testing helpers -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-mocks.js"></script>
</head>
<body>
<!-- bootstrap jasmine! -->
<script>
var jasmineEnv = jasmine.getEnv();
// Tell it to add an Html Reporter
// this will add detailed HTML-formatted results
// for each spec ran.
jasmineEnv.addReporter(new jasmine.HtmlReporter());
// Execute the tests!
jasmineEnv.execute();
</script>
</body>
</html>
PS: $ onChange
不是组件生命周期钩子的有效名称。 应为 $ onChanges
。
P.S.: $onChange
is not a valid name of the component's life-cycle hook. It should be $onChanges
.
这篇关于如何通过父元素测试组件绑定的更改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!