Reputation: 1220
Let'a assume a simple EmberJS component:
//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();
})
});
Now I would like to use integration tests to find out whether clicking the component reaches (delegates to) the doSth()
method.
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
});
The problem is that I cannot substitute method doSth()
with my mock.
Thus I never run into assert.ok()
statement.
I have tried to insert following statements onto the line 3 in the test:
// idea 1
this.set('doSth', function(){
assert.ok(true);
});
// idea 2
this.doSth = function(){
assert.ok(true);
};
None of the 2 approaches (idea 1, idea 2) worked. this.subject()
is also unavailable since it's an integration test, not a unit test.
Update:
Imagine doSth()
be a function like the openUrlInANewTab() shown below that does not influence the component's state, nor the associated controller's nor route's state.
Example:
//my-app/components/my-component.js
...
import openUrlInANewTab from 'my-app/utils/new-tab-opener';
...
export default Ember.Component.extend({
...
doSth(url){
openUrlInANewTab(url);
}
while
// my-app/utils/new-tab-opener.js
export default function openUrlInANewTab(){
anchor = document.createElement("a");
...
anchor.click();
...
}
Upvotes: 1
Views: 2454
Reputation: 3368
As an alternative to what Daniel had proposed you might consider using a closure action in case of handling click within your component. What I mean is instead of delegating to one of your methods (what you had done and could not test) or to a service (what Daniel suggested) just trigger an action passed to your component. This way also provides you the opportunity to mock in your test. Moreover, this approach results in a more reactive component; since you inject the behavior to your component from the owner at runtime; which enables you to reuse your custom component within different contexts (different parent components/templates) easily. See the twiddle for an example of what I mean.
Upvotes: 1
Reputation: 18692
You can move doSth
to service custom-service
that you inject to your my-component
. Then in test you can EASILY test this by injecting fake 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');
})
});
Upvotes: 3