Unit testing methods and computed properties follows previous patterns shown in Unit Testing Basics because Ember.Component extends Ember.Object.
Setup
Create the component to test using ember generate component pretty-color
:
This Ember component:
import layout from '../templates/components/pretty-color';
export default Ember.Component.extend({
layout: layout,
classNames: ['pretty-color'],
attributeBindings: ['style'],
style: function() {
return 'color: ' + this.get('name') + ';';
}.property('name')
});
... with its accompanying Handlebars template:
... can be unit tested using the moduleForComponent
helper.
This helper will find the component by name (pretty-color) and its template (if
available).
moduleForComponent('pretty-color', {
// specify the other units that are required for this test
// needs: ['component:foo', 'helper:bar']
});
Now each test following the moduleForComponent
call has a subject()
function,
which aliases the create method on the component factory.
We can test to make sure that changing the component's color property updates the rendered HTML:
test('changing colors', function(assert) {
// this.subject() is available because we used moduleForComponent
var component = this.subject();
// we wrap this with Ember.run because it is an async function
Ember.run(function() {
component.set('name','red');
});
// first call to $() renders the component.
assert.equal(this.$().attr('style'), 'color: red;');
// another async function, so we need to wrap it with Ember.run
Ember.run(function() {
component.set('name', 'green');
});
assert.equal(this.$().attr('style'), 'color: green;');
});
We might also test this component to ensure the template is being rendered properly.
test('template is rendered with the color name', function(assert) {
// this.subject() is available because we used moduleForComponent
var component = this.subject();
// first call to $() renders the component.
assert.equal($.trim(this.$().text()), 'Pretty Color:');
// we wrap this with Ember.run because it is an async function
Ember.run(function() {
component.set('name', 'green');
});
assert.equal($.trim(this.$().text()), 'Pretty Color: green');
});
Interacting with Components in the DOM
Ember Components are a great way to create powerful, interactive, self-contained custom HTML elements. Because of this, it is important to test the component's methods and the user's interaction with the component.
Let's create a very simple component that simply sets its own
title when clicked. Run ember generate component my-foo
and open the
component file:
import layout from '../templates/components/my-foo';
export default Ember.Component.extend({
layout: layout,
title:'Hello World',
actions: {
updateTitle: function() {
this.set('title', 'Hello Ember World');
}
}
});
Whose template is:
We would use jQuery triggers to interact with the rendered component and test its behavior:
moduleForComponent('my-foo', 'MyFooComponent');
test('clicking link updates the title', function(assert) {
var component = this.subject();
// assert default state
assert.equal(this.$().find('h2').text(), 'Hello World');
// perform click action
this.$().find('button').click();
assert.equal(this.$().find('h2').text(), 'Hello Ember World');
});
sendAction
validation in components
Components often utilize sendAction
, which is a way to interact with the Ember
application. Here's a simple component that sends the action internalAction
when a button is clicked:
import layout from '../templates/components/my-other-foo';
export default Ember.Component.extend({
layout: layout,
actions: {
doSomething: function() {
this.sendAction('internalAction');
}
}
});
The button can be found in the template:
In our test, we will create a test double (dummy object) that receives the action being sent by the component.
test('trigger external action when button is clicked', function(assert) {
assert.expect(1);
// component instance
var component = this.subject();
// render it
this.$();
var targetObject = {
externalAction: function() {
// we have the assertion here which will be
// called when the action is triggered
assert.ok(true, 'external Action was called!');
}
};
// setup a fake external action to be called when
// button is clicked
component.set('internalAction', 'externalAction');
// set the targetObject to our dummy object (this
// is where sendAction will send its action to)
component.set('targetObject', targetObject);
// click the button
this.$().find('button').click();
});