Open10
Reactテスト手法 調査
作業用プロジェクトを作成。
$ npx create-react-app demo
とりあえずテストを動かしてみる。
$ cd demo
$ yarn test
以下のように出力される。変更がない場合はテストは行われない。
No tests found related to files changed since last commit.
Press `a` to run all tests, or run Jest with `--watchAll`.
Watch Usage: Press w to show more.
適当にApp.jsを編集する
App.js
import logo from './logo.svg';
import './App.css';
function App() {
return <div className='App'></div>;
}
export default App;
テストに失敗する
FAIL src/App.test.js
✕ renders learn react link (29 ms)
● renders learn react link
TestingLibraryElementError: Unable to find an element with the text: /learn react/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
<body>
<div>
<div
class="App"
/>
</div>
</body>
4 | test('renders learn react link', () => {
5 | render(<App />);
> 6 | const linkElement = screen.getByText(/learn react/i);
| ^
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
at Object.getElementError (node_modules/@testing-library/dom/dist/config.js:37:19)
at node_modules/@testing-library/dom/dist/query-helpers.js:90:38
at node_modules/@testing-library/dom/dist/query-helpers.js:62:17
at getByText (node_modules/@testing-library/dom/dist/query-helpers.js:111:19)
at Object.<anonymous> (src/App.test.js:6:30)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 3.261 s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.
learn reactの文字を戻す
App.js
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className='App'>
<header className='App-header'>
<a
className='App-link'
href='https://reactjs.org'
target='_blank'
rel='noopener noreferrer'
>
Learn React
</a>
</header>
</div>
);
}
export default App;
テスト成功
PASS src/App.test.js
✓ renders learn react link (22 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.031 s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.
App.test.jsのようにtestがついているものがテストに使用される
App.test.js
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
testの中でErrorが起きるとテストに失敗する。
Shallow Renderingとは1階層だけ描写するもの
Shallow
<div id="sample-form">
<p>sample</p>
<InputComponent />
<SubmitComponent />
</div>
Mount
<div id="sample-form">
<div>
<p>sample</p>
<input type="text">
</div>
<button type="submit">
Submit
</button>
</div>
テスト対象を分離させるのに良い
Enzymeを使って超基本的なテストを書いてみる
App.test.js
import Enzyme, { shallow } from 'enzyme';
import EnzymeAdapter from '@wojtekmaj/enzyme-adapter-react-17';
import App from './App';
Enzyme.configure({ adapter: new EnzymeAdapter() });
test('renders component with enzyme.', () => {
const wrapper = shallow(<App />);
expect(wrapper.exists()).toBe(true);
});
テスト駆動開発をする。
先に期待する内容を列挙する。
App.test.js
import App from './App';
import Enzyme, { shallow } from 'enzyme';
import EnzymeAdapter from '@wojtekmaj/enzyme-adapter-react-17';
Enzyme.configure({ adapter: new EnzymeAdapter() });
test('renders withou error', () => {
// テストの内容
});
test('renders button', () => {});
test('renders text', () => {});
その後、テストの内容を書く。
App.test.js
import App from './App';
import Enzyme, { shallow } from 'enzyme';
import EnzymeAdapter from '@wojtekmaj/enzyme-adapter-react-17';
Enzyme.configure({ adapter: new EnzymeAdapter() });
test('renders withou error', () => {
const wrapeer = shallow(<App />);
const appComponent = wrapeer.find("[data-test='component-app']");
expect(appComponent.length).toBe(1);
});
この時点ではエラーが起きるのでテストをパスするように実装を行う。
App.js
import './App.css';
function App() {
return <div data-test='component-app'></div>;
}
export default App;
date-test='component-app'のようなテストのために用意した実質不要なプロパティを公開時に取り除く方法
まず必要なプラグインをインストールする。
$ yarn add -D babel-plugin-react-remove-properties
細かい設定が行えるようにejectする
$ yarn eject
yを選択する。
package.jsonのbabelに設定を追加
"babel": {
"env": {
"production": {
"plugins": [
["react-remove-properties", {"properties": ["data-test"]}]
]
}
},
"presets": [
"react-app"
]
}
buildを行い、実行してみる。
$ yarn build
$ npm install -g serve
$ serve -s build
ブラウザの開発者ツールで確認するとdata-testプロパティは消えた状態でページが表示される。
DRY: Don't Repeat Yourself
多用する箇所はfunction化すると良い
App.test.js
Enzyme.configure({ adapter: new EnzymeAdapter() });
// ↓こんな感じで説明を書くと親切
/**
* Factory function to create a Shallow Wrapper for the App component.
* @function setUp
* @returns {ShallowWrapper}
*/
const setUp = () => shallow(<App />);
const findByTestAttr = (wrapper, val) => wrapper.find(`[data-test='${val}']`);
test('renders withou error', () => {
const wrapper = setUp();
const appComponent = findByTestAttr(wrapper, 'component-app');
expect(appComponent.length).toBe(1);
});
ただしテストとして意味を持つように繰り返すのはOK
テストは時にドキュメントのような役目を持つ。
テキストの内容をテストする、ボタンを押すことを想定するなどの書き方は以下
App.test.js
test('counter display starts at 0', () => {
const wrapper = setUp();
const count = findByTestAttr(wrapper, 'count').text();
expect(count).toBe('0');
});
test('clicking button increments counter', () => {
const wrapper = setUp();
// ボタンを見つける
const button = findByTestAttr(wrapper, 'increment-button');
// ボタンを押す
button.simulate('click');
// カウンターを見つける
const count = findByTestAttr(wrapper, 'count').text();
// 数値が1になっているはず
expect(count).toBe('1');
});
最初に一度に要素を取得するのではなく、変化後をテストするのであれば都度要素を発見するようにする。