## 四、Enzyme庫
[Enzyme](https://github.com/airbnb/enzyme)是官方測試工具庫的封裝,它模擬了jQuery的API,非常直觀,易于使用和學習。
它提供三種測試方法。
* `shallow`
* `render`
* `mount`
### 4.1 shallow
[shallow](https://github.com/airbnb/enzyme/blob/master/docs/api/shallow.md)方法就是官方的shallow rendering的封裝。
下面是第一個[測試用例](https://github.com/ruanyf/react-testing-demo/blob/master/test/enzyme1.test.js#L6),測試`App`的標題。
~~~
import {shallow} from 'enzyme';
describe('Enzyme Shallow', function () {
it('App\'s title should be Todos', function () {
let app = shallow(<App/>);
expect(app.find('h1').text()).to.equal('Todos');
});
};
~~~
上面代碼中,`shallow`方法返回`App`的淺渲染,然后`app.find`方法找出`h1`元素,`text`方法取出該元素的文本。
關于`find`方法,有一個注意點,就是它只支持簡單選擇器,稍微復雜的一點的CSS選擇器都不支持。
~~~
component.find('.my-class'); // by class name
component.find('#my-id'); // by id
component.find('td'); // by tag
component.find('div.custom-class'); // by compound selector
component.find(TableRow); // by constructor
component.find('TableRow'); // by display name
~~~
### 4.2 render
[`render`](https://github.com/airbnb/enzyme/blob/master/docs/api/render.md)方法將React組件渲染成靜態的HTML字符串,然后分析這段HTML代碼的結構,返回一個對象。它跟`shallow`方法非常像,主要的不同是采用了第三方HTML解析庫Cheerio,它返回的是一個Cheerio實例對象。
下面是第二個[測試用例](https://github.com/ruanyf/react-testing-demo/blob/master/test/enzyme1.test.js#L13),測試所有Todo項的初始狀態。
~~~
import {render} from 'enzyme';
describe('Enzyme Render', function () {
it('Todo item should not have todo-done class', function () {
let app = render(<App/>);
expect(app.find('.todo-done').length).to.equal(0);
});
});
~~~
在上面代碼中,你可以看到,`render`方法與`shallow`方法的API基本是一致的。 Enzyme的設計就是,讓不同的底層處理引擎,都具有同樣的API(比如`find`方法)。
### 4.3 mount
[`mount`](https://github.com/airbnb/enzyme/blob/master/docs/api/mount.md)方法用于將React組件加載為真實DOM節點。
下面是第三個[測試用例](https://github.com/ruanyf/react-testing-demo/blob/master/test/enzyme1.test.js#L21),測試刪除按鈕。
~~~
import {mount} from 'enzyme';
describe('Enzyme Mount', function () {
it('Delete Todo', function () {
let app = mount(<App/>);
let todoLength = app.find('li').length;
app.find('button.delete').at(0).simulate('click');
expect(app.find('li').length).to.equal(todoLength - 1);
});
});
~~~
上面代碼中,`find`方法返回一個對象,包含了所有符合條件的子組件。在它的基礎上,`at`方法返回指定位置的子組件,`simulate`方法就在這個組件上觸發某種行為。
下面是第四個[測試用例](https://github.com/ruanyf/react-testing-demo/blob/master/test/enzyme1.test.js#L28),測試Todo項的點擊行為。
~~~
import {mount} from 'enzyme';
describe('Enzyme Mount', function () {
it('Turning a Todo item into Done', function () {
let app = mount(<App/>);
let todoItem = app.find('.todo-text').at(0);
todoItem.simulate('click');
expect(todoItem.hasClass('todo-done')).to.equal(true);
});
});
~~~
下面是第五個[測試用例](https://github.com/ruanyf/react-testing-demo/blob/master/test/enzyme1.test.js#L35),測試添加新的Todo項。
~~~
import {mount} from 'enzyme';
describe('Enzyme Mount', function () {
it('Add a new Todo', function () {
let app = mount(<App/>);
let todoLength = app.find('li').length;
let addInput = app.find('input').get(0);
addInput.value = 'Todo Four';
app.find('.add-button').simulate('click');
expect(app.find('li').length).to.equal(todoLength + 1);
});
});
~~~
### 4.4 API
下面是Enzyme的一部分API,你可以從中了解它的大概用法。
* `.get(index)`:返回指定位置的子組件的DOM節點
* `.at(index)`:返回指定位置的子組件
* `.first()`:返回第一個子組件
* `.last()`:返回最后一個子組件
* `.type()`:返回當前組件的類型
* `.text()`:返回當前組件的文本內容
* `.html()`:返回當前組件的HTML代碼形式
* `.props()`:返回根組件的所有屬性
* `.prop(key)`:返回根組件的指定屬性
* `.state([key])`:返回根組件的狀態
* `.setState(nextState)`:設置根組件的狀態
* `.setProps(nextProps)`:設置根組件的屬性