<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 總結 1. React 組件結構和JSX語法, 不適合傳統的測試工具 2. React 測試必須使用官方的測試工具庫, 但是由于使用不夠方便, 因此有了第三方庫. 其中 Airbnb公司的 Enzyme 最容易上手. 因此每個測試用例,至少都有兩種寫法. 3. React組件兩種形式:虛擬DOM對象, 真實DOM節點 1. Shadow Rendering : 測試虛擬DOM的方法 2. DOM Rendering : 測試真實DOM方法 4. DOM環境(即window, document 和 navigator 對象) 5. [推薦使用這種]Enzyme的語法和Jquery基本一樣, 使用起來很方便. 1. `shallow` 淺渲染 官方shallow rendering 的封裝 2.`render` 和 shallow語法一樣, 底層使用 Cheerio 3. `mount` 渲染到DOM --- >[React 測試入門教程](http://www.ruanyifeng.com/blog/2016/02/react-testing-tutorial.html) >作者: 阮一峰 >日期: 2016年2月13日 ![](https://box.kancloud.cn/2016-08-08_57a828f77aca4.png) 越來越多的人,使用React開發Web應用。它的測試就成了一個大問題。 React的組件結構和JSX語法,不適用傳統的測試工具,必須有新的測試方法和工具。 本文總結目前React測試的基本做法和最佳實踐,手把手教你如何寫React測試。 ## 一、Demo應用 請先安裝Demo。 ~~~ $ git clone https://github.com/ruanyf/react-testing-demo.git $ cd react-testing-demo && npm install $ npm start ~~~ 然后,打開 http://127.0.0.1:8080/,你會看到一個 Todo 應用。 ![](https://box.kancloud.cn/2016-08-08_57a828f7941e9.png) 接下來,我們就要測試這個應用,一共有5個測試點。 1. 應用標題應為"Todos" 2. Todo項的初始狀態("未完成"或"已完成")應該正確 3. 點擊一個Todo項,它就反轉狀態("未完成"變為"已完成",反之亦然) 4. 點擊刪除按鈕,該Todo項就被刪除 5. 點擊添加按鈕,會新增一個Todo項 這5個測試用例都已經寫好了,執行一下就可以看到結果。 ~~~ $ npm test ~~~ 下面就來看,測試用例應該怎么寫。測試框架我用的是Mocha,如果你不熟悉,可以先讀我寫的《Mocha教程》[**本序列上面有 Mocha教程**]。 ## 二、測試工具庫 React測試必須使用官方的測試工具庫,但是它用起來不夠方便,所以有人做了封裝,推出了一些第三方庫,其中Airbnb公司的[Enzyme](https://github.com/airbnb/enzyme)最容易上手。 這就是說,同樣的測試用例至少有兩種寫法,本文都將介紹。 1. 官方測試工具庫的寫法 2. Enzyme的寫法 ## 三、官方測試工具庫 我們知道,一個React組件有兩種存在形式:虛擬DOM對象(即React.Component的實例)和真實DOM節點。官方測試工具庫對這兩種形式,都提供測試解決方案。 1. `Shallow Rendering`:測試虛擬DOM的方法 2. `DOM Rendering`: 測試真實DOM的方法 ### 3.1 Shallow Rendering Shallow Rendering (淺渲染)指的是,將一個組件渲染成虛擬DOM對象,但是只渲染第一層,不渲染所有子組件,所以處理速度非常快。它不需要DOM環境,因為根本沒有加載進DOM。 首先,在測試腳本之中,引入官方測試工具庫。 ~~~ import TestUtils from 'react-addons-test-utils'; 然后,寫一個 Shallow Rendering 函數,該函數返回的就是一個淺渲染的虛擬DOM對象。 import TestUtils from 'react-addons-test-utils'; function shallowRender(Component) { const renderer = TestUtils.createRenderer(); renderer.render(<Component/>); return renderer.getRenderOutput(); } ~~~ 第一個測試用例,是測試標題是否正確。這個用例不需要與DOM互動,不涉及子組件,所以使用淺渲染非常合適。 ~~~ describe('Shallow Rendering', function () { it('App\'s title should be Todos', function () { const app = shallowRender(App); expect(app.props.children[0].type).to.equal('h1'); expect(app.props.children[0].props.children).to.equal('Todos'); }); }); ~~~ 上面代碼中,const app = shallowRender(App)表示對App組件進行"淺渲染",然后app.props.children[0].props.children就是組件的標題。 你大概會覺得,這個屬性的寫法太古怪了,但實際上是有規律的。每一個虛擬DOM對象都有props.children屬性,它包含一個數組,里面是所有的子組件。app.props.children[0]就是第一個子組件,在我們的例子中就是h1元素,它的props.children屬性就是h1的文本。 第二個測試用例,是測試Todo項的初始狀態。 首先,需要修改shallowRender函數,讓它接受第二個參數。 ~~~ import TestUtils from 'react-addons-test-utils'; function shallowRender(Component, props) { const renderer = TestUtils.createRenderer(); renderer.render(<Component {...props}/>); return renderer.getRenderOutput(); } ~~~ 下面就是測試用例。 ~~~ import TodoItem from '../app/components/TodoItem'; describe('Shallow Rendering', function () { it('Todo item should not have todo-done class', function () { const todoItemData = { id: 0, name: 'Todo one', done: false }; const todoItem = shallowRender(TodoItem, {todo: todoItemData}); expect(todoItem.props.children[0].props.className.indexOf('todo-done')).to.equal(-1); }); }); ~~~ 上面代碼中,由于TodoItem是App的子組件,所以淺渲染必須在TodoItem上調用,否則渲染不出來。在我們的例子中,初始狀態反映在組件的Class屬性(props.className)是否包含todo-done。 ### 3.2 renderIntoDocument 官方測試工具庫的第二種測試方法,是將組件渲染成真實的DOM節點,再進行測試。這時就需要調用renderIntoDocument 方法。 ~~~ import TestUtils from 'react-addons-test-utils'; import App from '../app/components/App'; const app = TestUtils.renderIntoDocument(<App/>); ~~~ renderIntoDocument 方法要求存在一個真實的DOM環境,否則會報錯。因此,測試用例之中,DOM環境(即window, document 和 navigator 對象)必須是存在的。jsdom 庫提供這項功能。 ~~~ import jsdom from 'jsdom'; if (typeof document === 'undefined') { global.document = jsdom.jsdom('<!doctype html><html><body></body></html>'); global.window = document.defaultView; global.navigator = global.window.navigator; } ~~~ 將上面這段代碼,保存在test子目錄下,取名為 setup.js。然后,修改package.json。 ~~~ { "scripts": { "test": "mocha --compilers js:babel-core/register --require ./test/setup.js", }, } ~~~ 現在,每次運行npm test,setup.js 就會包含在測試腳本之中一起執行。 第三個測試用例,是測試刪除按鈕。 ~~~ describe('DOM Rendering', function () { it('Click the delete button, the Todo item should be deleted', function () { const app = TestUtils.renderIntoDocument(<App/>); let todoItems = TestUtils.scryRenderedDOMComponentsWithTag(app, 'li'); let todoLength = todoItems.length; let deleteButton = todoItems[0].querySelector('button'); TestUtils.Simulate.click(deleteButton); let todoItemsAfterClick = TestUtils.scryRenderedDOMComponentsWithTag(app, 'li'); expect(todoItemsAfterClick.length).to.equal(todoLength - 1); }); }); ~~~ 上面代碼中,第一步是將App渲染成真實的DOM節點,然后使用scryRenderedDOMComponentsWithTag方法找出app里面所有的li元素。然后,取出第一個li元素里面的button元素,使用TestUtils.Simulate.click方法在該元素上模擬用戶點擊。最后,判斷剩下的li元素應該少了一個。 這種測試方法的基本思路,就是找到目標節點,然后觸發某種動作。官方測試工具庫提供多種方法,幫助用戶找到所需的DOM節點。 | 方法名 | 說明 | | -- | -- | |scryRenderedDOMComponentsWithClass |找出所有匹配指定className的節點| |findRenderedDOMComponentWithClass|與scryRenderedDOMComponentsWithClass用法相同,但只返回一個節點,如有零個或多個匹配的節點就報錯| |scryRenderedDOMComponentsWithTag|找出所有匹配指定標簽的節點| | findRenderedDOMComponentWithTag | 與scryRenderedDOMComponentsWithTag用法相同,但只返回一個節點,如有零個或多個匹配的節點就報錯| |scryRenderedComponentsWithType|找出所有符合指定子組件的節點| |findRenderedComponentWithType|與scryRenderedComponentsWithType用法相同,但只返回一個節點,如有零個或多個匹配的節點就報錯| |findAllInRenderedTree|遍歷當前組件所有的節點,只返回那些符合條件的節點| 可以看到,上面這些方法很難拼寫,好在還有另一種找到DOM節點的替代方法。 ### 3.3 findDOMNode 如果一個組件已經加載進入DOM,react-dom模塊的findDOMNode方法會返回該組件所對應的DOM節點。 我們使用這種方法來寫第四個測試用例,用戶點擊Todo項時的行為。 ~~~ import {findDOMNode} from 'react-dom'; describe('DOM Rendering', function (done) { it('When click the Todo item,it should become done', function () { const app = TestUtils.renderIntoDocument(<App/>); const appDOM = findDOMNode(app); const todoItem = appDOM.querySelector('li:first-child span'); let isDone = todoItem.classList.contains('todo-done'); TestUtils.Simulate.click(todoItem); expect(todoItem.classList.contains('todo-done')).to.be.equal(!isDone); }); }); ~~~ 上面代碼中,findDOMNode方法返回App所在的DOM節點,然后找出第一個li節點,在它上面模擬用戶點擊。最后,判斷classList屬性里面的todo-done,是否出現或消失。 第五個測試用例,是添加新的Todo項。 ~~~ describe('DOM Rendering', function (done) { it('Add an new Todo item, when click the new todo button', function () { const app = TestUtils.renderIntoDocument(<App/>); const appDOM = findDOMNode(app); let todoItemsLength = appDOM.querySelectorAll('.todo-text').length; let addInput = appDOM.querySelector('input'); addInput.value = 'Todo four'; let addButton = appDOM.querySelector('.add-todo button'); TestUtils.Simulate.click(addButton); expect(appDOM.querySelectorAll('.todo-text').length).to.be.equal(todoItemsLength + 1); }); }); ~~~ 上面代碼中,先找到input輸入框,添加一個值。然后,找到Add Todo按鈕,在它上面模擬用戶點擊。最后,判斷新的Todo項是否出現在Todo列表之中。 findDOMNode方法的最大優點,就是支持復雜的CSS選擇器。這是TestUtils本身不提供的。 ## 四、Enzyme庫 Enzyme是官方測試工具庫的封裝,它模擬了jQuery的API,非常直觀,易于使用和學習。 它提供三種測試方法。 > shallow > render > mount ### 4.1 shallow shallow方法就是官方的shallow rendering的封裝。 下面是第一個測試用例,測試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方法將React組件渲染成靜態的HTML字符串,然后分析這段HTML代碼的結構,返回一個對象。它跟shallow方法非常像,主要的不同是采用了第三方HTML解析庫Cheerio,它返回的是一個Cheerio實例對象。 下面是第二個測試用例,測試所有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方法用于將React組件加載為真實DOM節點。 下面是第三個測試用例,測試刪除按鈕。 ~~~ 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方法就在這個組件上觸發某種行為。 下面是第四個測試用例,測試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); }); }); ~~~ 下面是第五個測試用例,測試添加新的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):設置根組件的屬性 (完)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看