Unit testing methods and computed properties follows previous patterns shown in Unit Testing Basics because Ember.Component extends Ember.Object.
Components can be tested using the moduleForComponent
helper.
Let's assume we have a component with a style
property that is updated
whenever the value for its name
property changes. The style
attribute of the
component is bound to its style
property.
You can follow along by generating your own component with
ember generate component pretty-color
.
import Ember from 'ember';
export default Ember.Component.extend({
attributeBindings: ['style'],
style: Ember.computed('name', function() {
return 'color: ' + this.get('name') + ';';
})
});
The moduleForComponent
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']
});
Each test following the moduleForComponent
call has access to the subject()
function, which lets us create a new instance of the component, as well as
provide any initial values we want it to have.
We can test that changing the component's name
property updates the
component's style
attribute and is reflected in the rendered HTML:
test('changing colors', function(assert) {
assert.expect(2);
// this.subject() is available because we used moduleForComponent
var component = this.subject({ name: 'red' });
// Renders the component to the page
this.render();
// Assert the initial style
assert.equal(this.$().attr('style'), 'color: red;');
// We wrap this with Ember.run because this.set is an async function
Ember.run(function() {
// Change the name
component.set('name', 'green');
});
// Assert the style has changed
assert.equal(this.$().attr('style'), 'color: green;');
});
We might also test this component to ensure that the content of its template is being rendered properly:
test('template is rendered with the color name', function(assert) {
assert.expect(2);
// this.subject() is available because we used moduleForComponent
var component = this.subject();
// Renders the component to the page
this.render();
// Assert initial content of the component
var initialContent = $.trim(this.$().text());
assert.equal(initialContent, 'Pretty Color:');
// we wrap this with Ember.run because it is an async function
Ember.run(function() {
component.set('name', 'green');
});
// Assert content of the component has changed
var finalContent = $.trim(this.$().text());
assert.equal(finalContent, 'Pretty Color: green');
});
Testing User Interaction
Components are a great way to create powerful, interactive, and self-contained custom HTML elements. It is important to test the component's methods and the user's interaction with the component.
Imagine you have the following component that changes its title when a button is clicked on:
You can follow along by generating your own component with
ember generate component magic-title
.
import Ember from 'ember';
export default Ember.Component.extend({
title: 'Hello World',
actions: {
updateTitle: function() {
this.set('title', 'This is Magic');
}
}
});
jQuery triggers can be used to simulate user interaction and test that the title is updated when the button is clicked on:
test('clicking the button updates the title', function(assert) {
assert.expect(2);
// Create the component instance
var component = this.subject();
// Assert the initial title
var initialTitle = this.$().find('h2').text();
assert.equal(initialTitle, 'Hello World');
// Click on the button
this.$().find('button').click();
// Assert that the title has changed
var finalTitle = this.$().find('h2').text();
assert.equal(finalTitle, 'Hello Ember World');
});
Testing Actions
Components often utilize the sendAction()
method to send actions to other
objects in your application.
For example, imagine you have a comment form component that sends a specified
submit
action when the form is submitted, passing along the form's data:
You can follow along by generating your own component with
ember generate component comment-form
.
import Ember from 'ember';
export default Ember.Component.extend({
body: null,
actions: {
submit: function() {
var body = this.get('body');
this.sendAction('submit', { body: body });
}
}
});
You might use this component in your application like this:
Here's an example test that asserts that the specified externalAction
action
is sent when the component's internal submit
action is triggered by making use
of a test double (dummy object):
test('external action is triggered when form is submitted', function(assert) {
// This is important to make sure that the test fails if
// our assertion is never called
assert.expect(1);
// Create our test double
var targetObject = {
externalAction: function(attributes) {
// This assertion will be called when the action is triggered
assert.deepEqual(attributes, { body: 'You are not a wizard!' });
}
};
// Creates the component
var component = this.subject({
// Sets sample data
body: 'You are not a wizard!',
// Sets the targetObject to our test double
// (this is where sendAction will send its action)
targetObject: targetObject,
// Specifies which action to send to targetObject on submit
submit: 'externalAction',
});
// Renders the component to the page
this.render();
// Submits the form
this.$().find('input[type="submit"]').click();
});