📖
TDDのTを作ってみました
mediba Advent Calendar 2023 23日目の記事です。
最近バックエンドでのテスト駆動開発(以下TDD)を体験したyangです。
バックエンド且つGOの初心者で、自信を持ってPRを出せる訳は「テストは全部通った」ですねw
今までフロントエンドではさまざまな理由でTDDをやっていなかったが、今回は言い訳なしで、
TDDをやっていきたいと思います!
作り
これから仮で作りたいものは、
ECサイトの商品リスト、機能は三つ
- 商品リスト表示
- 商品名で検索
- 商品価額範囲で検索
TDDのサイクルを回すには、まずはテストコードを書くことです。
仕様が決まったら、普通?ではデザイナーさんからデザインをもらってから、開発を始めるが、今回は仕様だけでも進めるようにテストを書いてみます。
とはいえ、テストしたいものを明確にしなければならない、なので、現在テストで担保したいものは上記三つの機能ですね、それに機能を実現する最低限のコンポネートは下記のように
- 商品リスト表示
- リスト(product_list)
- 商品名で検索
- 商品名入力欄(name_search_input)
- 検索ボタン(search_button)
- 商品価格で検索
- 商品価格範囲入力欄(price_search_input_min&max)
- 検索ボタン(search_button)
それぞれのコンポネートの存在確認していきます、
※今回のテストはjest
と@testing-library/react
でやります。
test('コンポネートの存在確認', () => {
render(<MyProductList />); //仮コンポネート MyProductList
expect(screen.getByTestId('product_list')).toBeInTheDocument();
expect(screen.getByTestId('name_search_input')).toBeInTheDocument();
expect(screen.getByTestId('search_button')).toBeInTheDocument();
expect(screen.getByTestId('price_search_input_min')).toBeInTheDocument();
expect(screen.getByTestId('price_search_input_max')).toBeInTheDocument();
});
それから三つの機能に関するテストは下記のように書きます。
// 仮商品データ
const products = [
{ id: 1, name: 'Chooclate', price: 1000 },
{ id: 2, name: 'Banaba', price: 898 },
{ id: 3, name: 'Carort', price: 354 },
{ id: 4, name: 'Paenats', price: 12 },
];
// 機能①
test('商品リスト表示', () => {
render(<ProductList products={products} />);
// 商品一つずつを表示しているか
expect(screen.getAllByTestId('product')).toHaveLength(4);
// 表示したい項目が表示されているか
expect(screen.getByText(/Chooclate/i)).toBeInTheDocument();
expect(screen.getByText(/¥1000/i)).toBeInTheDocument();
expect(screen.getByText(/Banaba/i)).toBeInTheDocument();
expect(screen.getByText(/¥898/i)).toBeInTheDocument();
expect(screen.getByText(/Carort/i)).toBeInTheDocument();
expect(screen.getByText(/¥354/i)).toBeInTheDocument();
expect(screen.getByText(/Paenats/i)).toBeInTheDocument();
expect(screen.getByText(/¥12/i)).toBeInTheDocument();
});
// 機能②
test('商品名で検索', () => {
render(<ProductList products={products} />);
// 入力エレメント取得
const nameInput = screen.getByTestId('name_search_input');
// 'a'で検索したら
fireEvent.change(nameInput, { target: { value: 'a' } });
// 商品名に'a'が入っている商品だけ表示されているか
expect(screen.getAllByTestId('product')).toHaveLength(4);
expect(screen.getByTestId('product_list')).toBeInTheDocument();
expect(screen.getByText(/Chooclate/i)).toBeInTheDocument();
expect(screen.getByText(/Banaba/i)).toBeInTheDocument();
expect(screen.getByText(/Paenats/i)).toBeInTheDocument();
expect(screen.getByText(/Carort/i)).toBeInTheDocument();
});
// 機能③
test('商品価額範囲で検索', () => {
render(<ProductList products={products} />);
// 価格範囲入力エレメント取得
const minPriceInput = screen.getByTestId('price_search_input_min');
const maxPriceInput = screen.getByTestId('price_search_input_max');
// '400'で最小価格に設定
fireEvent.change(minPriceInput, { target: { value: '400' } });
// 値段が400円以上も商品が表示さているか
expect(screen.getAllByTestId('product')).toHaveLength(2);
expect(screen.getByText(/Chooclate/i)).toBeInTheDocument();
expect(screen.getByText(/Banaba/i)).toBeInTheDocument();
expect(screen.queryByText(/Carort/i)).not.toBeInTheDocument();
expect(screen.queryByText(/Paenats/i)).not.toBeInTheDocument();
// '900'で最大価格に設定
fireEvent.change(maxPriceInput, { target: { value: '900' } });
// 値段が400円以上且900円以下も商品が表示さているか
expect(screen.getAllByTestId('product')).toHaveLength(1);
expect(screen.queryByText(/Chooclate/i)).not.toBeInTheDocument();
expect(screen.queryByText(/Carort/i)).not.toBeInTheDocument();
expect(screen.queryByText(/Paenats/i)).not.toBeInTheDocument();
expect(screen.getByText(/Banaba/i)).toBeInTheDocument();
});
これでテストコードが完成しました。
感想
今までと違って、書いたコードの上でテストを作成するではなく、仕様をコード化にしものであります。
自然に仕様通りに動くかというテストになり、そもそも開発はこれからのものだから、実装の詳細をテストするようなことになりません。
これからもこいう風にTDDをやっていきたいという気持ちです。
上記のテストで作ってみた投稿お待ちしております
参考:
[1] 《When I follow TDD》: https://kentcdodds.com/blog/when-i-follow-tdd
[2] Testing Library: https://testing-library.com/
猫自慢
Discussion