Ember集成测试 #
一、集成测试概述 #
集成测试用于测试组件的渲染和交互行为,验证组件是否正确工作。
1.1 生成测试 #
bash
ember generate component-test user-card
ember generate integration-test user-card
1.2 测试结构 #
javascript
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | user-card', function (hooks) {
setupRenderingTest(hooks);
test('it renders', async function (assert) {
await render(hbs`<UserCard />`);
assert.dom(this.element).hasText('');
});
});
二、渲染测试 #
2.1 基本渲染 #
javascript
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | user-card', function (hooks) {
setupRenderingTest(hooks);
test('it renders user information', async function (assert) {
this.set('user', {
name: 'John Doe',
email: 'john@example.com',
});
await render(hbs`<UserCard @user={{this.user}} />`);
assert.dom('.user-name').hasText('John Doe');
assert.dom('.user-email').hasText('john@example.com');
});
});
2.2 条件渲染 #
javascript
test('it shows email when showEmail is true', async function (assert) {
this.set('user', { name: 'John', email: 'john@example.com' });
this.set('showEmail', true);
await render(hbs`<UserCard @user={{this.user}} @showEmail={{this.showEmail}} />`);
assert.dom('.user-email').exists();
this.set('showEmail', false);
assert.dom('.user-email').doesNotExist();
});
2.3 列表渲染 #
javascript
test('it renders list of items', async function (assert) {
this.set('items', [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
{ id: 3, name: 'Item 3' },
]);
await render(hbs`<ItemList @items={{this.items}} />`);
assert.dom('.item').exists({ count: 3 });
assert.dom('.item:first-child').hasText('Item 1');
});
三、交互测试 #
3.1 点击测试 #
javascript
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, click } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | counter', function (hooks) {
setupRenderingTest(hooks);
test('it increments on click', async function (assert) {
await render(hbs`<Counter />`);
assert.dom('.count').hasText('0');
await click('.increment');
assert.dom('.count').hasText('1');
await click('.increment');
assert.dom('.count').hasText('2');
});
});
3.2 输入测试 #
javascript
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, fillIn } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | search-input', function (hooks) {
setupRenderingTest(hooks);
test('it updates value on input', async function (assert) {
await render(hbs`<SearchInput />`);
await fillIn('input', 'search term');
assert.dom('input').hasValue('search term');
});
test('it calls onSearch on Enter', async function (assert) {
assert.expect(1);
this.set('handleSearch', (term) => {
assert.strictEqual(term, 'search term');
});
await render(hbs`<SearchInput @onSearch={{this.handleSearch}} />`);
await fillIn('input', 'search term');
await triggerKeyEvent('input', 'keyup', 'Enter');
});
});
3.3 表单测试 #
javascript
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, fillIn, click } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | login-form', function (hooks) {
setupRenderingTest(hooks);
test('it submits form with credentials', async function (assert) {
assert.expect(2);
this.set('handleLogin', (credentials) => {
assert.deepEqual(credentials, {
email: 'test@example.com',
password: 'password123',
});
});
await render(hbs`<LoginForm @onSubmit={{this.handleLogin}} />`);
await fillIn('[data-test-email]', 'test@example.com');
await fillIn('[data-test-password]', 'password123');
await click('[data-test-submit]');
});
});
四、异步测试 #
4.1 等待渲染 #
javascript
import { settled } from '@ember/test-helpers';
test('it handles async state', async function (assert) {
this.set('isLoading', true);
await render(hbs`<LoadingSpinner @isLoading={{this.isLoading}} />`);
assert.dom('.spinner').exists();
this.set('isLoading', false);
await settled();
assert.dom('.spinner').doesNotExist();
});
4.2 等待动画 #
javascript
import { waitFor } from '@ember/test-helpers';
test('it shows modal after animation', async function (assert) {
await render(hbs`<Modal @isOpen={{true}} />`);
await waitFor('.modal-content');
assert.dom('.modal-content').isVisible();
});
五、测试助手 #
5.1 自定义测试助手 #
javascript
// tests/helpers/login.js
import { fillIn, click } from '@ember/test-helpers';
export async function login(email, password) {
await fillIn('[data-test-email]', email);
await fillIn('[data-test-password]', password);
await click('[data-test-submit]');
}
javascript
// tests/acceptance/dashboard-test.js
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { visit, currentURL } from '@ember/test-helpers';
import { login } from '../helpers/login';
module('Acceptance | dashboard', function (hooks) {
setupApplicationTest(hooks);
test('visiting dashboard after login', async function (assert) {
await visit('/login');
await login('test@example.com', 'password');
assert.strictEqual(currentURL(), '/dashboard');
});
});
六、DOM断言 #
6.1 qunit-dom断言 #
javascript
// 存在性
assert.dom('.element').exists();
assert.dom('.element').exists({ count: 3 });
// 文本内容
assert.dom('.element').hasText('Hello');
assert.dom('.element').hasText(/Hello/);
assert.dom('.element').includesText('Hello');
// 属性
assert.dom('input').hasValue('test');
assert.dom('a').hasAttribute('href', '/path');
assert.dom('button').hasClass('active');
assert.dom('button').isDisabled();
// 可见性
assert.dom('.element').isVisible();
assert.dom('.element').isNotVisible();
// 焦点
assert.dom('input').isFocused();
七、最佳实践 #
7.1 使用data-test属性 #
handlebars
{{! 组件}}
<button data-test-submit type="submit">提交</button>
<input data-test-email type="email" />
javascript
// 测试
await click('[data-test-submit]');
await fillIn('[data-test-email]', 'test@example.com');
7.2 测试用户行为 #
javascript
// 好的做法 - 测试用户行为
test('user can submit form', async function (assert) {
await fillIn('[data-test-email]', 'test@example.com');
await click('[data-test-submit]');
assert.dom('.success-message').exists();
});
// 避免 - 测试实现细节
test('form has correct state', async function (assert) {
assert.strictEqual(component.isValid, true);
});
八、总结 #
集成测试要点:
| 方法 | 用途 |
|---|---|
| render | 渲染组件 |
| click | 点击元素 |
| fillIn | 填写输入 |
| triggerKeyEvent | 触发键盘事件 |
| settled | 等待异步完成 |
集成测试是验证组件行为的重要手段。
最后更新:2026-03-28