【React+TypeScript】ミニマムなユニットテストコードを書いてみた
はじめに
React+TypeScriptでフロントエンドを触るようになりましたが、いまだにテストは全然自動化できてません…
テストの種類も色々あり、どれも気になるのですが、よくあるテストピラミッドを参考にまずはユニットテストを考えることにしました。
そもそもユニットテストのテストコードとはどんなものなのかを知りたかったので、ミニマムなテストコードを書いてみました。
参考サイト様
主にこちらの記事のユニットテストを実施させていただきました。
JavaScriptテストフレームワーク「Jest」を使って、Reactをテストしよう – ajike developer's blog – UXデザイン会社アジケの開発者ブログ
その他、テストについて参考にさせていただきました。
React Testing Libraryの使い方 - Qiita
react-testing-frameworkで要素が「存在しないこと」を確認しようとするとエラーになる - 幸福なプログラマ
使うもの
React
TypeScript
Reactのテストユーティリティ
テストユーティリティ – React
やったこと
新規Reactアプリケーションを作成する
下記コマンドで新規プロジェクトを作成します。
npx create-react-app [プロジェクト名] --typescript
テスト対象のコンポーネントを作る
テスト対象のコンポーネントを作ります。
今回はLink.tsxです。
import React, { Component } from 'react'; class Link extends Component<{ text?: string }, {}> { render() { return ( <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > {this.props.text || 'No contents'} </a> ) } } export default Link
App.tsxで呼び出します。
import React from 'react'; import logo from './logo.svg'; import './App.css'; import Link from './components/Link'; function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.tsx</code> and save to reload. </p> <Link text='this link'/> </header> </div> ); } export default App;
テストフォルダを作る
テストコードを格納するためにフォルダ名「tests」のフォルダを作ります。
テストファイルを作る
testsフォルダにテストファイルを作ります。
今回はLink.text.tsx。
テストファイルの書き方についてもまだ掘り下げ切れていないのですが、
確認項目ひとつにつき、actひとつという感じのようです。
今回は、propsが渡された場合と渡されない場合の2パターンでテストします。
propsが渡されない場合は、textが'No contents'であること
propsが渡された場合は、textが'this link'であることを確認しています。
import { render } from 'react-dom'; import { act } from 'react-dom/test-utils'; import Link from '../components/Link'; describe('Link component testing', () => { let container: Element | DocumentFragment | null = null; container = document.createElement("div"); document.body.appendChild(container); it('Linkコンポーネントテスト', () => { // ケース別にテストします act(() => { render(<Link />, container); }); // propsが渡されない場合 expect(container!.textContent).toBe('No contents'); act(() => { render(<Link text='this link'/>, container); }); // propsが渡された場合 expect(container!.textContent).toBe('this link'); }); })
App.test.tsxの編集
create-react-appすると、デフォルトでApp.test.tsxというテストファイルが作られますが、
そのまま使用するとエラーが出るので、下記のように書き換えるorコメントアウトします。
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(); const linkElement = screen.queryByText(/learn react/i); expect(linkElement).toBeNull(); });
テストの実行
ターミナルで下記コマンドを実行して、テスト開始します。
npm test
問題がなければ2 passedの結果が出ます。
意図的に失敗させたい場合は、Link.text.tsx内の'this link'を別の文字列にしたりします。
そうするとこんな感じのエラーが出ます。
ソースコード全体
今回作ったもののソースコードはGitHubにあげているの、そちらも参照ください。
github.com
感想
今回は2つしかテストがないので、結果が見やすいですがテストが増えたら見づらくなるのでは?と思ってます。
たぶんVScodeの拡張機能を使ったりすればもっと見やすいテスト結果の取得ができるんだろうなという気がします。
今回のやり方では必要なコンポーネントごとにテストコードを書くのは結構な労力になることがわかりました。
この考え方をベースにしてユニットテストコードを書くのは果たして正解なのか…
ユニットテストコードを書くことはどんなやり方でもこれくらい大変なのか、
またはもっと簡単なやり方があるのか、慣れなのか…ううーむ
少なくともテストコードの作成には労力が結構必要ということが身をもってわかったので、必要なところにピンポイントで用意することの大切さはわかった気がします。
(むやみやたらにテストコードを用意すればいいものではない、コスト的にも難しい…等
多少大変でも品質向上のために適材適所なテストコードは必要かと思っているので
とりあえず導入できるくらいのテストコードは模索したいと思います。