模拟组件函数以在 EmberJS 中进行组件集成测试
Mocking component function for the purposes of component integration test in EmberJS
让我们假设一个简单的 EmberJS 组件:
//my-app/components/my-component.js
export default Ember.Component.extend({
classNames: ['cursor-pointer'],
doSth(){
// whatever, state of the component does not change
},
clickListener: Ember.on('click', function(){
this.doSth();
})
});
现在我想使用集成测试来确定单击组件是否到达(委托)doSth()
方法。
moduleForComponent('my-component', 'Integration | Component | stores parser previewer', {
integration: true
});
test('should call doSth() on click', function (assert) {
/* Line 3 - the place I tried to set mocked doSth() up */
this.render(hbs`{{my-component}}`);
this.$('.cursor-pointer').click(); // triggers clickListener() correctly
});
问题是我不能用我的 mock 替换方法 doSth()
。
因此我从不 运行 进入 assert.ok()
语句。
我试图在测试中的第 3 行插入以下语句:
// idea 1
this.set('doSth', function(){
assert.ok(true);
});
// idea 2
this.doSth = function(){
assert.ok(true);
};
2 种方法(想法 1、想法 2)中的 None 有效。 this.subject()
也不可用,因为它是集成测试,而不是单元测试。
更新:
想象 doSth()
是一个像下面所示的 openUrlInANewTab() 这样的函数,它不影响组件的状态,也不影响关联的控制器或路由的状态。
示例:
//my-app/components/my-component.js
...
import openUrlInANewTab from 'my-app/utils/new-tab-opener';
...
export default Ember.Component.extend({
...
doSth(url){
openUrlInANewTab(url);
}
而
// my-app/utils/new-tab-opener.js
export default function openUrlInANewTab(){
anchor = document.createElement("a");
...
anchor.click();
...
}
您可以将 doSth
移动到您注入到 my-component
的服务 custom-service
。然后在测试中,您可以通过注入虚假服务轻松测试它。
tests/integration/components/my-component-test.js:
test('should call doSth() on click', function(assert) {
const customStub = Ember.Service.extend({
doSth() {
// whatever, state of the component does not change
console.log('Stubbed doSth fired');
assert.ok(true, 'Stubbed doSth fired');
}
});
this.register('service:custom-service', customStub);
this.inject.service('custom-service', { as: 'customService' });
this.render(hbs`{{my-component}}`);
this.$('.cursor-pointer').click(); // triggers clickListener() correctly
});
components/my-component.js:
import Ember from 'ember';
export default Ember.Component.extend({
customService: Ember.inject.service('custom-service'),
classNames: ['cursor-pointer'],
clickListener: Ember.on('click', function(){
this.get('customService').doSth();
console.log('Click listener');
})
});
作为 Daniel 提议的替代方法,您可以考虑在组件内处理点击时使用关闭操作。我的意思是不是委托给你的方法之一(你已经完成但无法测试)或服务(丹尼尔建议的)只是触发传递给你的组件的动作。这种方式还为您提供了在测试中模拟的机会。此外,这种方法会产生更具反应性的组件;因为您在运行时将行为从所有者注入到您的组件中;这使您能够轻松地在不同的上下文(不同的父 components/templates)中重用您的自定义组件。有关我的意思的示例,请参阅 twiddle。
让我们假设一个简单的 EmberJS 组件:
//my-app/components/my-component.js
export default Ember.Component.extend({
classNames: ['cursor-pointer'],
doSth(){
// whatever, state of the component does not change
},
clickListener: Ember.on('click', function(){
this.doSth();
})
});
现在我想使用集成测试来确定单击组件是否到达(委托)doSth()
方法。
moduleForComponent('my-component', 'Integration | Component | stores parser previewer', {
integration: true
});
test('should call doSth() on click', function (assert) {
/* Line 3 - the place I tried to set mocked doSth() up */
this.render(hbs`{{my-component}}`);
this.$('.cursor-pointer').click(); // triggers clickListener() correctly
});
问题是我不能用我的 mock 替换方法 doSth()
。
因此我从不 运行 进入 assert.ok()
语句。
我试图在测试中的第 3 行插入以下语句:
// idea 1
this.set('doSth', function(){
assert.ok(true);
});
// idea 2
this.doSth = function(){
assert.ok(true);
};
2 种方法(想法 1、想法 2)中的 None 有效。 this.subject()
也不可用,因为它是集成测试,而不是单元测试。
更新:
想象 doSth()
是一个像下面所示的 openUrlInANewTab() 这样的函数,它不影响组件的状态,也不影响关联的控制器或路由的状态。
示例:
//my-app/components/my-component.js
...
import openUrlInANewTab from 'my-app/utils/new-tab-opener';
...
export default Ember.Component.extend({
...
doSth(url){
openUrlInANewTab(url);
}
而
// my-app/utils/new-tab-opener.js
export default function openUrlInANewTab(){
anchor = document.createElement("a");
...
anchor.click();
...
}
您可以将 doSth
移动到您注入到 my-component
的服务 custom-service
。然后在测试中,您可以通过注入虚假服务轻松测试它。
tests/integration/components/my-component-test.js:
test('should call doSth() on click', function(assert) {
const customStub = Ember.Service.extend({
doSth() {
// whatever, state of the component does not change
console.log('Stubbed doSth fired');
assert.ok(true, 'Stubbed doSth fired');
}
});
this.register('service:custom-service', customStub);
this.inject.service('custom-service', { as: 'customService' });
this.render(hbs`{{my-component}}`);
this.$('.cursor-pointer').click(); // triggers clickListener() correctly
});
components/my-component.js:
import Ember from 'ember';
export default Ember.Component.extend({
customService: Ember.inject.service('custom-service'),
classNames: ['cursor-pointer'],
clickListener: Ember.on('click', function(){
this.get('customService').doSth();
console.log('Click listener');
})
});
作为 Daniel 提议的替代方法,您可以考虑在组件内处理点击时使用关闭操作。我的意思是不是委托给你的方法之一(你已经完成但无法测试)或服务(丹尼尔建议的)只是触发传递给你的组件的动作。这种方式还为您提供了在测试中模拟的机会。此外,这种方法会产生更具反应性的组件;因为您在运行时将行为从所有者注入到您的组件中;这使您能够轻松地在不同的上下文(不同的父 components/templates)中重用您的自定义组件。有关我的意思的示例,请参阅 twiddle。