Unit testing methods and computed properties follows previous patterns shown in Unit Testing Basics because Ember.Component extends Ember.Object.
单元测试方案和计算属性与之前单元测试基础中说明的相同,因为Ember.Component
集成自Ember.Object
。
Setup
设置
Before testing components, be sure to add testing application div to your testing html file:
在测试组件之前,需要确定测试应用的div
已经加到测试的html文件中:
<!-- as of time writing, ID attribute needs to be named exactly ember-testing -->
<div id="ember-testing"></div>
and then you’ll also need to tell Ember to use this element for rendering the application in
此外还需要通知Ember
使用这个元素来重新渲染应用。
App.rootElement = '#ember-testing'
Components can be tested using the moduleForComponent
helper. Here is a
simple Ember component:
组件可以使用moduleForComponent
助手来进行测试。下面是一个简单的Ember
控件:
App.PrettyColorComponent = Ember.Component.extend({
classNames: ['pretty-color'],
attributeBindings: ['style'],
style: function() {
return 'color: ' + this.get('name') + ';';
}.property('name')
});
with an accompanying Handlebars template:
其对应的Handlebars
模板:
Pretty Color: {{name}}
Unit testing this component can be done using the moduleForComponent
helper.
This helper will find the component by name (pretty-color) and it’s template (if
available).
对这个控件进行单元测试可以使用moduleForComponent
助手。助手将通过名称来查找这个组件(pretty-color)和其模板(如果存在)。
moduleForComponent('pretty-color');
Now each of our tests has a function subject()
which aliases the create
method on the component factory.
现在每个测试有了一个subject()
函数,该函数是组件工厂的create
方法的一个别名。
Here’s how we would test to make sure rendered HTML changes when changing the color on the component:
下面是测试改变组件的颜色后,看看HTML是否重新渲染了。
test('changing colors', function(){
// 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.
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');
});
equal(this.$().attr('style'), 'color: green;');
});
Another test that we might perform on this component would be to ensure the template is being rendered properly.
另外一个可能的测试是检查组件的模板是否被正确地渲染了。
test('template is rendered with the color name', function(){
// this.subject() is available because we used moduleForComponent
var component = this.subject();
// first call to $() renders the component.
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');
});
equal($.trim(this.$().text()), 'Pretty Color: green');
});
Live Example
在线示例
Interacting with Components in the DOM
与DOM中的组件交互
Ember Components are a great way to create powerful, interactive, self-contained custom HTML elements. Because of this, it is important to not only test the methods on the component itself, but also the user’s interaction with the component.
Ember组件可以方便的用来创建有力的、交互的、自包含的自定义HTML元素。正因为此,不仅测试组件自身的方法很重要,测试用户与组件的交互也一样的重要。
Let’s look at a very simple component which does nothing more than set it’s own title when clicked:
下面看一个非常简单的组件,该组件只是在被点击时设置自己的标题:
App.MyFooComponent = Em.Component.extend({
title:'Hello World',
actions:{
updateTitle: function(){
this.set('title', 'Hello Ember World');
}
}
});
We would use Integration Test Helpers to interact with the rendered component:
下面将使用集成测试助手来与渲染的组件进行交互。
moduleForComponent('my-foo', 'MyFooComponent');
test('clicking link updates the title', function() {
var component = this.subject();
// append the component to the DOM
this.append();
// assert default state
equal(find('h2').text(), 'Hello World');
// perform click action
click('button');
andThen(function() { // wait for async helpers to complete
equal(find('h2').text(), 'Hello Ember World');
});
});
Live Example
在线示例
Components with built in layout
具有内置布局的组件
Some components do not use a separate template. The template can be embedded into the component via the layout property. For example:
一些组件并不适用一个分离的模板。模板可以通过布局属性嵌入在组件代码中。例如:
App.MyFooComponent = Ember.Component.extend({
// layout supercedes template when rendered
layout: Ember.Handlebars.compile(
"<h2>I'm a little {{noun}}</h2><br/>" +
"<button {{action 'clickFoo'}}>Click Me</button>"
),
noun: 'teapot',
actions:{
changeName: function(){
this.set('noun', 'embereño');
}
}
});
In this example, we would still perform our test by interacting with the DOM.
在本例中,也将对于组件的交互进行测试。
moduleForComponent('my-foo', 'MyFooComponent');
test('clicking link updates the title', function() {
var component = this.subject();
// append the component to the DOM
this.append();
// assert default state
equal(find('h2').text(), "I'm a little teapot");
// perform click action
click('button');
andThen(function() { // wait for async helpers to complete
equal(find('h2').text(), "I'm a little embereño");
});
});
Live Example
在线示例
Testing Components with Built-in Layout
Programmatically interacting with components
程序化组件交互
Another way we can test our components is to perform function calls directly on the component instead of through DOM interaction. Let’s use the same code example we have above as our component, but perform the tests programatically:
另外一种测试组件的方法是通过直接调用组件函数来取代与DOM进行交互。这里采用与之前相同的例子作为测试的对象,不同的是通过编码直接进行测试:
moduleForComponent('my-foo', 'MyFooComponent');
test('clicking link updates the title', function() {
var component = this.subject();
// append the component to the DOM, returns DOM instance
var $component = this.append();
// assert default state
equal($component.find('h2').text(), "I'm a little teapot");
// send action programmatically
Em.run(function(){
component.send('changeName');
});
equal($component.find('h2').text(), "I'm a little embereño");
});
Live Example
在线示例
Programatically Testing Components
sendAction
validation in components
组件中sendAction
验证
Components often utilize sendAction
, which is a way to interact with the Ember
application. Here’s a simple component which sends the action internalAction
when a button is clicked:
组件经常使用sendAction
,这是与Ember应用交互的一种方式。下面是一个简单的组件,当一个按钮被点击时,发送internalAction
操作:
App.MyFooComponent = Ember.Component.extend({
layout:Ember.Handlebars.compile("<button {{action 'doSomething'}}></button>"),
actions:{
doSomething: function(){
this.sendAction('internalAction');
}
}
});
In our test, we will create a dummy object that receives the action being sent by the component.
在测试中,将创建一个僵尸对象,用来接收组件发送的操作。
moduleForComponent('my-foo', 'MyFooComponent');
test('trigger external action when button is clicked', function() {
// tell our test to expect 1 assertion
expect(1);
// component instance
var component = this.subject();
// component dom instance
var $component = this.append();
var targetObject = {
externalAction: function(){
// we have the assertion here which will be
// called when the action is triggered
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 it's action to)
component.set('targetObject', targetObject);
// click the button
click('button');
});
Live Example
在线示例
sendAction Validation in Components
Components Using Other Components
使用其他组件的组件
Sometimes components are easier to maintain when broken up into parent and child components. Here is a simple example:
有时候将组件拆分为父子组件更容易维护,下面是一个简单的例子:
App.MyAlbumComponent = Ember.Component.extend({
tagName: 'section',
layout: Ember.Handlebars.compile(
"<section>" +
" <h3>{{title}}</h3>" +
" {{yield}}" +
"</section>"
),
titleBinding: ['title']
});
App.MyKittenComponent = Ember.Component.extend({
tagName: 'img',
attributeBindings: ['width', 'height', 'src'],
src: function() {
return 'http://placekitten.com/' + this.get('width') + '/' + this.get('height');
}.property('width', 'height')
});
Usage of this component might look something like this:
使用这个组件可能如下面代码所示:
{{#my-album title="Cats"}}
{{my-kitten width="200" height="300"}}
{{my-kitten width="100" height="100"}}
{{my-kitten width="50" height="50"}}
{{/my-album}}
Testing components like these which include child components is very simple using
the needs
callback.
通过needs
回调来测试这些包含子组件的组件非常容易。
moduleForComponent('my-album', 'MyAlbumComponent', {
needs: ['component:my-kitten']
});
test('renders kittens', function() {
expect(2);
// component instance
var component = this.subject({
template: Ember.Handlebars.compile(
'{{#my-album title="Cats"}}' +
' {{my-kitten width="200" height="300"}}' +
' {{my-kitten width="100" height="100"}}' +
' {{my-kitten width="50" height="50"}}' +
'{{/my-album}}'
)
});
// append component to the dom
var $component = this.append();
// perform assertions
equal($component.find('h3:contains("Cats")').length, 1);
equal($component.find('img').length, 3);
});