React コンポーネントのテストを書く時に考えることと、テストコードサンプル
こんにちは、Gaji-Labo フロントエンドエンジニアの鈴木です。
普段 React + TypeScript でコンポーネントを実装する時に、テストコードを書くようにしているのですが、その際に考えていることと、実際のテストコードについて書いていきます。
テストの必要性について
以前に「テストコード書いた方がいいよね、でもテストするってどういうことだろう?」という内容のブログを書きました。
今回はそのような内容には触れませんが、もし興味があれば「コンポーネント開発でテストを書くための意識作り」も合わせてご覧ください。
ではさっそく、実際にどのようなテストを書いているのか掘り下げていきたいと思います。
なにをテストするか
コンポーネントがあった方が分かりやすいので、以下のボタンコンポーネントを例として進めていきます。
interface Props {
className?: string;
isDisabled?: boolean;
isSmall?: boolean;
onClick?: () => void;
label: string;
}
const SomethingButton: FC<Props> = ({
className,
isDisabled,
isSmall,
onClick,
label,
}) => {
const classes = classNames(
css.SomethingButton,
{ [css.isSmall]: isSmall },
className,
);
return (
<button
className={classes}
disabled={isDisabled}
onClick={onClick}
type="button"
>
{label}
</button>
);
};
このようなコンポーネントがあった場合、以下の内容をテストしたいと考えます。
- コンポーネントの役割が意図通り機能しているか
- スタイルの変化を持つ場合、それが適用されているか
それを具体的なテスト方法に落とし込むと以下のようになります。
label
のテキストが表示されているかonClick
に渡した関数が呼ばれるかisDisabled
がtrue
の時にdisabled
属性が渡っているかisSmall
がtrue
の時に クラス名.isSmall
が付与されているか
それぞれのテストの書き方
スナップショットテストとユニットテストを使い分けて、それぞれテストしていきます。
スナップショットテスト
テストする項目
label
のテキストが表示されているか
テストをするポイント
- テキストが表示されていることが分かれば OK
スナップショットテストで全テストをカバーするのではなく、コンポーネントのレンダリング結果が意図通りであることを確認できれば十分だと考えています。
import { shallow } from 'enzyme';
import * as React from 'react';
import SomethingButton from '../SomethingButton';
describe('<SomethingButton />', () => {
it('renders correctly', () => {
const wrapper = shallow(<SomethingButton label="button label" />);
expect(wrapper).toMatchSnapshot();
});
});
ユニットテスト
テストする項目
onClick
に渡した関数が呼ばれるかisDisabled
がtrue
の時にdisabled
属性が渡っているかisSmall
がtrue
の時に クラス名.isSmall
が付与されているか
いくつかあるので一つずつテストコードを書いていきます。
onClick
に渡した関数が呼ばれるか
describe('onClick', () => {
it('should calls', () => {
const mockOnClick = jest.fn();
const wrapper = shallow(
<SomethingButton label="button label" onClick={mockOnClick} />,
);
wrapper.simulate('click');
expect(mockOnClick).toHaveBeenCalled();
});
});
このテストではモック関数を使用しています。toHaveBeenCalled
を使用することでモック関数が呼ばれたかを確認できます。
参考リンク
- https://jestjs.io/docs/ja/jest-objec
- https://deltice.github.io/jest/docs/ja/expect.html#tohavebeencalled
isDisabled
が true
の時に disabled
属性が渡っているか
describe('with [disabled=true]', () => {
it('should have `disabled` attribute', () => {
const wrapper = shallow(
<SomethingButton label="button label" isDisabled={true} />,
);
expect(wrapper.find('.SomethingButton').prop('disabled')).toBeTruthy();
});
});
prop('disabled')
を使用して disabled
属性が渡っているかをテストします。
参考リンク
isSmall
が true
の時に クラス名 .isSmall
が付与されているか
describe('with [isSmall=true]', () => {
it('should have `.isSmall`', () => {
const wrapper = shallow(
<SomethingButton label="button label" isSmall={true} />,
);
expect(wrapper.find('.SomethingButton').hasClass('isSmall')).toBeTruthy();
});
});
hasClass
を使用して CSS クラス名 isSmall
が存在することをテストします。
参考リンク
テスト全体で意識していること
どのテストでも共通して意識しているポイントは以下の2つです。
テストに必要のないオプショナルな props は渡さない
例えば onClick
に渡した関数が呼ばれるかのテストで isSmall
を渡さない、といった感じです。テストに必要ないものは渡さず、テストしたいものだけを持っている状態にします。
何がテストされたいか分かるようにする
テストコードを書いている本人はテストしたい内容を分かっているので問題ありませんが、誰が読んでも「このテストでなにを期待しているか」が分かるように意識しています。
まとめ
普段どのようにコンポーネントのテストを書いているのか掘り下げてみました。
なにをどのようにテストするのか、1つずつ噛み砕いていくと、それぞれのテストもコンパクトに書けると思います。
また、どうテストするかのパターンをたくさん知っているとテストしやすいと感じています。他の人の書いたテストコードを見るのは発見も多く、「そうかけるのか〜!」と見ていて楽しいです。
Gaji-Labo は React 開発の実績と知見があります
フロントエンド開発の専門家である私たちが御社の開発チームに入ることで、バックエンドも含めた全体の開発効率が上がります。
「人手が足りず信頼できるエンジニアを探している」
「Vue.js から React へリプレイスを検討している」
「フロントエンドの効率化をどうしたらいいか分からない」
「自分たちで手を付けてみたがいまいち上手くいかない」
フロントエンド開発に関わるお困りごとがあれば、まずは一度お気軽に Gaji-Labo にご相談ください。
オンラインでのヒアリングとフルリモートでのプロセス支援にも対応しています。
フロントエンドの相談をする!