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文件中:

  1. <!-- as of time writing, ID attribute needs to be named exactly ember-testing -->
  2. <div id="ember-testing"></div>

and then you’ll also need to tell Ember to use this element for rendering the application in

此外还需要通知Ember使用这个元素来重新渲染应用。

  1. App.rootElement = '#ember-testing'

Components can be tested using the moduleForComponent helper. Here is a simple Ember component:

组件可以使用moduleForComponent助手来进行测试。下面是一个简单的Ember控件:

  1. App.PrettyColorComponent = Ember.Component.extend({
  2. classNames: ['pretty-color'],
  3. attributeBindings: ['style'],
  4. style: function() {
  5. return 'color: ' + this.get('name') + ';';
  6. }.property('name')
  7. });

with an accompanying Handlebars template:

其对应的Handlebars模板:

  1. 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)和其模板(如果存在)。

  1. 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是否重新渲染了。

  1. test('changing colors', function(){
  2. // this.subject() is available because we used moduleForComponent
  3. var component = this.subject();
  4. // we wrap this with Ember.run because it is an async function
  5. Ember.run(function(){
  6. component.set('name','red');
  7. });
  8. // first call to $() renders the component.
  9. equal(this.$().attr('style'), 'color: red;');
  10. // another async function, so we need to wrap it with Ember.run
  11. Ember.run(function(){
  12. component.set('name', 'green');
  13. });
  14. equal(this.$().attr('style'), 'color: green;');
  15. });

Another test that we might perform on this component would be to ensure the template is being rendered properly.

另外一个可能的测试是检查组件的模板是否被正确地渲染了。

  1. test('template is rendered with the color name', function(){
  2. // this.subject() is available because we used moduleForComponent
  3. var component = this.subject();
  4. // first call to $() renders the component.
  5. equal($.trim(this.$().text()), 'Pretty Color:');
  6. // we wrap this with Ember.run because it is an async function
  7. Ember.run(function(){
  8. component.set('name', 'green');
  9. });
  10. equal($.trim(this.$().text()), 'Pretty Color: green');
  11. });

Live Example

在线示例

Unit Testing Components

组件单元测试

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:

下面看一个非常简单的组件,该组件只是在被点击时设置自己的标题:

  1. App.MyFooComponent = Em.Component.extend({
  2. title:'Hello World',
  3. actions:{
  4. updateTitle: function(){
  5. this.set('title', 'Hello Ember World');
  6. }
  7. }
  8. });

We would use Integration Test Helpers to interact with the rendered component:

下面将使用集成测试助手来与渲染的组件进行交互。

  1. moduleForComponent('my-foo', 'MyFooComponent');
  2. test('clicking link updates the title', function() {
  3. var component = this.subject();
  4. // append the component to the DOM
  5. this.append();
  6. // assert default state
  7. equal(find('h2').text(), 'Hello World');
  8. // perform click action
  9. click('button');
  10. andThen(function() { // wait for async helpers to complete
  11. equal(find('h2').text(), 'Hello Ember World');
  12. });
  13. });

Live Example

在线示例

Unit Testing Components

组件单元测试

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:

一些组件并不适用一个分离的模板。模板可以通过布局属性嵌入在组件代码中。例如:

  1. App.MyFooComponent = Ember.Component.extend({
  2. // layout supercedes template when rendered
  3. layout: Ember.Handlebars.compile(
  4. "<h2>I'm a little {{noun}}</h2><br/>" +
  5. "<button {{action 'clickFoo'}}>Click Me</button>"
  6. ),
  7. noun: 'teapot',
  8. actions:{
  9. changeName: function(){
  10. this.set('noun', 'embereño');
  11. }
  12. }
  13. });

In this example, we would still perform our test by interacting with the DOM.

在本例中,也将对于组件的交互进行测试。

  1. moduleForComponent('my-foo', 'MyFooComponent');
  2. test('clicking link updates the title', function() {
  3. var component = this.subject();
  4. // append the component to the DOM
  5. this.append();
  6. // assert default state
  7. equal(find('h2').text(), "I'm a little teapot");
  8. // perform click action
  9. click('button');
  10. andThen(function() { // wait for async helpers to complete
  11. equal(find('h2').text(), "I'm a little embereño");
  12. });
  13. });

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进行交互。这里采用与之前相同的例子作为测试的对象,不同的是通过编码直接进行测试:

  1. moduleForComponent('my-foo', 'MyFooComponent');
  2. test('clicking link updates the title', function() {
  3. var component = this.subject();
  4. // append the component to the DOM, returns DOM instance
  5. var $component = this.append();
  6. // assert default state
  7. equal($component.find('h2').text(), "I'm a little teapot");
  8. // send action programmatically
  9. Em.run(function(){
  10. component.send('changeName');
  11. });
  12. equal($component.find('h2').text(), "I'm a little embereño");
  13. });

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操作:

  1. App.MyFooComponent = Ember.Component.extend({
  2. layout:Ember.Handlebars.compile("<button {{action 'doSomething'}}></button>"),
  3. actions:{
  4. doSomething: function(){
  5. this.sendAction('internalAction');
  6. }
  7. }
  8. });

In our test, we will create a dummy object that receives the action being sent by the component.

在测试中,将创建一个僵尸对象,用来接收组件发送的操作。

  1. moduleForComponent('my-foo', 'MyFooComponent');
  2. test('trigger external action when button is clicked', function() {
  3. // tell our test to expect 1 assertion
  4. expect(1);
  5. // component instance
  6. var component = this.subject();
  7. // component dom instance
  8. var $component = this.append();
  9. var targetObject = {
  10. externalAction: function(){
  11. // we have the assertion here which will be
  12. // called when the action is triggered
  13. ok(true, 'external Action was called!');
  14. }
  15. };
  16. // setup a fake external action to be called when
  17. // button is clicked
  18. component.set('internalAction', 'externalAction');
  19. // set the targetObject to our dummy object (this
  20. // is where sendAction will send it's action to)
  21. component.set('targetObject', targetObject);
  22. // click the button
  23. click('button');
  24. });

Live Example

在线示例

sendAction Validation in Components

组件中sendAction验证

Components Using Other Components

使用其他组件的组件

Sometimes components are easier to maintain when broken up into parent and child components. Here is a simple example:

有时候将组件拆分为父子组件更容易维护,下面是一个简单的例子:

  1. App.MyAlbumComponent = Ember.Component.extend({
  2. tagName: 'section',
  3. layout: Ember.Handlebars.compile(
  4. "<section>" +
  5. " <h3>{{title}}</h3>" +
  6. " {{yield}}" +
  7. "</section>"
  8. ),
  9. titleBinding: ['title']
  10. });
  11. App.MyKittenComponent = Ember.Component.extend({
  12. tagName: 'img',
  13. attributeBindings: ['width', 'height', 'src'],
  14. src: function() {
  15. return 'http://placekitten.com/' + this.get('width') + '/' + this.get('height');
  16. }.property('width', 'height')
  17. });

Usage of this component might look something like this:

使用这个组件可能如下面代码所示:

  1. {{#my-album title="Cats"}}
  2. {{my-kitten width="200" height="300"}}
  3. {{my-kitten width="100" height="100"}}
  4. {{my-kitten width="50" height="50"}}
  5. {{/my-album}}

Testing components like these which include child components is very simple using the needs callback.

通过needs回调来测试这些包含子组件的组件非常容易。

  1. moduleForComponent('my-album', 'MyAlbumComponent', {
  2. needs: ['component:my-kitten']
  3. });
  4. test('renders kittens', function() {
  5. expect(2);
  6. // component instance
  7. var component = this.subject({
  8. template: Ember.Handlebars.compile(
  9. '{{#my-album title="Cats"}}' +
  10. ' {{my-kitten width="200" height="300"}}' +
  11. ' {{my-kitten width="100" height="100"}}' +
  12. ' {{my-kitten width="50" height="50"}}' +
  13. '{{/my-album}}'
  14. )
  15. });
  16. // append component to the dom
  17. var $component = this.append();
  18. // perform assertions
  19. equal($component.find('h3:contains("Cats")').length, 1);
  20. equal($component.find('img').length, 3);
  21. });

Live Example

在线示例

Components with Embedded Components

带嵌套组件的组件