建立好 E2E 的環境後,就拿 TodoMVC 的樣板來做練習,以下為練習實作中的筆記
準備要測試的 TodoMVC
-
將 TodoMVC 的 template 和 css 搬進 Angular 專案中
-
建立一個練行用的 e2e spec file
1
2
3
4
5
6
7
8describe('Main', () => {
it('Visits the initial project page', () => {
cy.visit('/');
cy.contains('Welcome');
cy.contains('e2e-study app is running!');
});
}); -
執行 ng e2e,預期會看到測試失敗
-
將測試檔案修正成綠燈
1
2
3
4
5
6describe('Main', () => {
it('Visits the initial project page', () => {
cy.visit('/');
cy.contains('todos');
});
});
測試案例 1
檢查 Todo List 筆數是否為兩筆
-
html 結構
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24<section class="main">
<input id="toggle-all" class="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
<!-- These are here just to show the structure of the list items -->
<!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
<li class="completed">
<div class="view">
<input class="toggle" type="checkbox" checked>
<label>Taste JavaScript</label>
<button class="destroy"></button>
</div>
<input class="edit" value="Create a TodoMVC template">
</li>
<li>
<div class="view">
<input class="toggle" type="checkbox">
<label>Buy a unicorn</label>
<button class="destroy"></button>
</div>
<input class="edit" value="Rule the web">
</li>
</ul>
</section> -
測試案例 - 紅燈
1
2
3
4
5
6it('has two todo item in the list', () => {
cy.visit('/');
cy.get('.todo-list').then((ele) => {
expect(ele.children.length).to.equal(1);
});
}); -
綠燈
1
2
3
4
5
6it('has two todo item in the list', () => {
cy.visit('/');
cy.get('.todo-list').then((ele) => {
expect(ele.children.length).to.equal(2);
});
}); -
查了一下官方文件,有另外一種更乾淨的寫法
1
2
3
4it('has two todo item in the list', () => {
cy.visit('/');
cy.get('.todo-list').children().should('have.length', 2);
});
測試案例 2
新增 Todo,列表會多一筆紀錄
-
先重構一下 spec code,發現在兩個 test case 內有重複的 code,將其移到
beforeEach
內1
2
3
4
5
6
7
8
9
10
11
12
13
14describe('Main', () => {
beforeEach(() => {
cy.visit('/');
});
it('Visits the initial project page', () => {
cy.contains('todos');
});
it('has two todo item in the list', () => {
cy.get('.todo-list').children().should('have.length', 2);
});
}); -
在 input 地方輸入並按下 enter,期待會看到 3 筆 (預設 2 筆 + 新增 1 筆),預期失敗,因為沒有實作功能
1
2
3
4it('add new todo, list should have 3 items', () => {
cy.get('.new-todo').type('abc').type('{enter}');
cy.get('.todo-list').children().should('have.length', 3);
}); -
實作功能
- app.component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25import { Component } from '@angular/core';
import { v4 as uuidv4 } from 'uuid';
type Todo = {
id: string;
content: string;
isComplete: boolean;
};
({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
todos: Todo[] = [];
addTodo(ele: HTMLInputElement) {
this.todos.push({
id: uuidv4(),
content: ele.value,
isComplete: false,
});
ele.value = '';
}
}- app.component.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<header class="header">
<h1>todos</h1>
<input class="new-todo" placeholder="What needs to be done?" autofocus #newTodo (keyup.enter)="addTodo(newTodo)">
</header>
<!-- This section should be hidden by default and shown when there are todos -->
<section class="main">
<input id="toggle-all" class="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
...
<li *ngFor="let todo of todos">
<div class="view">
<input class="toggle" type="checkbox">
<label>{{ todo.content }}</label>
<button class="destroy"></button>
</div>
<input class="edit" [value]="todo.content">
</li>
</ul>
</section> -
重新執行測試就會看到綠燈了
-
整理 html 並配合調整測試檔案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20describe('Main', () => {
beforeEach(() => {
cy.visit('/');
});
it('Visits the initial project page', () => {
cy.contains('todos');
});
it('has two todo item in the list', () => {
cy.get('.todo-list').children().should('have.length', 0);
});
it('add new todo, list should have 3 items', () => {
cy.get('.new-todo').type('abc').type('{enter}');
cy.get('.todo-list').children().should('have.length', 1);
});
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<header class="header">
<h1>todos</h1>
<input class="new-todo" placeholder="What needs to be done?" autofocus #newTodo (keyup.enter)="addTodo(newTodo)">
</header>
<!-- This section should be hidden by default and shown when there are todos -->
<section class="main">
<input id="toggle-all" class="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
<!-- These are here just to show the structure of the list items -->
<!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
<li *ngFor="let todo of todos">
<div class="view">
<input class="toggle" type="checkbox">
<label>{{ todo.content }}</label>
<button class="destroy"></button>
</div>
<input class="edit" [value]="todo.content">
</li>
</ul>
</section>
小技巧
有時候在定位 HTMLElement 的時候會很麻煩,不像這個範例很單純,這時候就可透過 attribute 的方式來標註,根據這份 best practice 內提到,我們可以使用 data-cy
、data-test
、data-testid
來標註,就可以很精準地拿到我們想要的 element.
1 | <input class="new-todo" placeholder="What needs to be done?" autofocus #newTodo (keyup.enter)="addTodo(newTodo)" data-cy="newTodo"> |
1 | // 原本寫法 |