Visionalグループ 株式会社ビズリーチでは、2023年4月に入社した新卒プロダクト職(エンジニア/デザイナー)を対象とした新卒研修を約3ヶ月の間実施しました。最初の約1ヶ月間はビジネス職と合同で顧客志向を中心に学び、その後はプロダクト職としてモノづくりのプロセスや品質の基礎を学びました。

研修を通して得た学びや変化について、受講した社員が3回にわたりご紹介します。

本記事では、テスト駆動開発(TDD)の日本での第一人者として知られる和田卓人(@t_wada)さんを講師としてお招きし、品質の大切さを学んだ「TDDワークショップ」について、プロダクト職(エンジニア)の渋谷がお伝えします。

TDDワークショップの概要

ワークショップの構成

TDDとはプログラム実装前にテストコードを書き、そのテストに適合するようにコードを実装する開発手法です。今回のTDDワークショップでは、@t_wadaさんがTDDとは何か、どのように進めれば良いかなどについて解説されている動画を事前に視聴し事前学習が済んだ状態からスタートしました。 当日は「TDDの講義とライブコーディングによる実演」「ペアプログラミングでの実践」の2部構成になっており、TDDの進め方とコードの改善方法を学びました。

参加者の属性

自動テストについてある程度知識がある人と初めて学ぶ人が混ざっており、後半のペアプログラミング実践では自動テスト経験者と未経験者がペアとなってワークショップを進めました。

初めてのTDD

まず@t_wadaさんの講義を受けて学んだのは「常にプログラムが『動作すること』を保証する」と「ステップバイステップで開発を進める」がTDDの基本だということです。

常にプログラムが「動作すること」を保証する

TDDのゴールは「動作するきれいなコード」を実装することであり、その動作することの保証が「テストケースがパスしていること」です。この状態をいち早く作り出すためには、テストを先に書き、コードを検証できる環境を先に作るテストファーストの考え方が重要であることを教えていただきました。

TDDでは以下のプロセスに従って開発を進めます。

  1. 目標を考え、テストTODOを作成する
  2. 目標を示すテストを作成する
  3. そのテストを実行して失敗させる(Red)
  4. 次にテストが通るよう実装する
  5. 作成したテストを成功させる(Green)
  6. テストが通っている状態を維持したまま、実装したコードを改修する(Refactor)
  7. 1-6を繰り返す

まずテストケースをRedからGreenにした後、そのテストケースをRedにすることなくリファクタリングしていきます。テストケースがGreenである限り、プログラムが目的の動作をすることが保証されており、エンジニアは安心してコードに変更を加えられるようになります。テストを実装の後に書く方針では保証されている状態が最後に来てしまい、意図通りに動作しているか確信ができません。

ワークショップを通じてテストコードを先に書くことでエンジニアは自信を持ってコードを改善できるようになることを実感し、テストファーストの有効性を学びました。

ステップバイステップで開発を進める

一方で、ただプロセスに従って進めるだけではうまくいかないこともあります。複雑な条件分岐やロジックが必要なプログラムを実装する場合はプロセスやテスト対象を分割して開発を進めることも教わりました。簡単な実装であれば、

といった流れで実装します。

一方、難しい実装の場合、

というようにプロセスを分割して、テストケースに漏れがないか、正しく実装できているかを確認しながら実装します。 更にオブジェクトやコンポーネントの構成を設計する段階でテスト対象を「テスト容易性が高く、重要度が高いもの」と「テスト容易性が低く、重要でないもの」に分割し、前者を優先的に実装していきます。テスト対象を分割するデザインパターンとしてオブジェクトの中にあるロジックを外に出してテストしやすくするHumble Objectパターンがあることも講義の中で紹介していただき、どうテスト対象を分割していけば良いか学びました。

Humble Objectパターンの図

どうリファクタリングするか

@t_wadaさんによるライブコーディングではFizzBuzz問題を解くコードを例に、実装とテストコードの両方をどのようにリファクタリングしていくかについても解説していただきました。

ライブコーディングの様子

テストコードも負債になる

自動テストを書く上で意識すべきこととして「テストコードも負債になる」ことがあります。分かりにくいテストを書いてしまうと実装コードと同じように維持に手間がかかってしまうため、実装者は保守性の高いテストを意識してテストコードを書く必要があります。

保守性の高いテストを書く方法としてまず習ったのがテストの構造化です。前提条件や期待する動作でテストケースをグルーピングし、ツリー構造にすることで、どこに何のテストがあるか分かりやすくなります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
- 数を文字列に変換する
    - 1を渡すと文字列"1"を返す
    - 2を渡すと文字列"2"を返す

- 3の倍数のときは数の代わりに「Fizz」に変換する
    - 3を渡すと文字列"Fizz"を返す
    - 6を渡すと文字列"Fizz"を返す

- 5の倍数のときは数の代わりに「Buzz」に変換する
    - 5を渡すと文字列"Buzz"を返す
    - 10を渡すと文字列"Buzz"を返す

他にもテストケースを必要最小限にして保守の対象となるコードを減らす方法がありますが、ある程度のテストケース数が必要な場合もあります。ワークショップでは「テストケースを減らさず、コード量を減らす」方法の1つとして「パラメータ化テスト」を活用する方法も教えていただきました。

以下のコードはJavaとJUnit5を利用した例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// パラメータ化テストを使わない場合
@Nested
class _3の倍数のときは数の代わりにFizzに変換する {

    @Test
    void _3の倍数3を渡すと文字列Fizzを返す() {
        assertEquals("Fizz", fizzbuzz.convert(3));
    }

    @Test
    void _3の倍数6を渡すと文字列Fizzを返す() {
        assertEquals("Fizz", fizzbuzz.convert(6));
    }

    @Test
    void _3の倍数9を渡すと文字列Fizzを返す() {
        assertEquals("Fizz", fizzbuzz.convert(9));
    }
}

// パラメータ化テストを利用した場合
@ParameterizedTest(name = "_3の倍数{0}を渡すと文字列Fizzを返す")
@ValueSource(ints = {3, 6, 9})
void _3の倍数のときは数の代わりにFizzに変換する(int input) {
    assertEquals("Fizz", fizzbuzz.convert(input));
}

紹介された技法を使い、ライブコーディングでテストコードがどんどん分かりやすく改善されていく様子は見ていてとても興味深かったです。

後にコードを見る人のために何を残すか

@t_wadaさんのライブコーディングは、後にコードを見る人のためにどんな情報があると良いかを意識して進められていました。その中でも大事なこととして教わったのはコードコメントに何を残すかです。

コードをリファクタリングしていくと、やってみたけど上手く行かなかった実装方法も出てくると思います。他の人がリファクタリングする時に同じ実装方法を試して時間を浪費しないよう、コードコメントには「何をやらなかったか」「なぜやらなかったのか」を記述します。

結果として上手く実装できなかったとしても、その失敗を無駄にせずコードコメントとして残すことで次の実装者に「この方法ではうまく行かない」と伝えられます。

コードコメント1つ取っても情報として何を残すかを考え、質の高いアウトプットにしようとする姿勢を見て細部にこだわる意識を持つようになりました。

ペアプログラミング体験

ワークショップ後半はペアプログラミングでTDDでのコード実装を体験しました。ワークショップ参加後に自分たちで自発的にペアプロを行うようになるほど、そのメリットと楽しさを感じました。

ペアプログラミングの様子

自分の考えを声に出す

ペアプロではコードを書くドライバーとアドバイスをするナビゲーターに分かれ二人三脚でコードを実装していきます。互いに自分の考えを伝えるために会話していきますが、その中で「自分の考えを声に出して話す」ことによって考えが整理され、何に悩んでいるかがわかったり、良い解決策が浮かんだりしました。

実際にペアプロをやってみると1人で実装するより悩む時間が少なくなり、スラスラと実装が進んでいく楽しさを実感しました。

もし自分の考えを説明する相手がいないときはアヒルの人形などのモノに対して話しかける形でも良いことも教わりました。この方法はラバーダック・デバッグと呼ばれています。

ペアプロを通じて互いにスキルを学ぶ

冒頭で述べた通り、今回は自動テスト経験者と未経験者がペアとなってペアプロを進めました。この組み合わせは@t_wadaさんが意図したもので、ペアプロはスキル伝授にも非常に有効なことを体験しました。

教わる側は経験者がコード実装しながら何を考えているのか、何を選択し逆に何を選択しないのかといったリアルタイムの判断を間近で見れます。役割を交代した後も、未経験者が何を考えながら実装しているかが可視化されるため、教える側がより的確なアドバイスを与えられるようになります。

一方経験者側も相手に教えることで勉強になったと感じた人が多かったです。私の場合、テストを実装した経験があったので、ペアの相手にどうテストを実装するといいのかを教えるようにしました。相手に教えるなかで、逆に自分が理解していることとしていないことが明確になり、理解していないことについてはペアと一緒に考え、学ぶことができました。

デザイナーから見たTDD

最後にデザイナーがTDDワークショップを受けて何を感じたかを、共にワークショップを受講した堀池よりご紹介します。

TDDワークショップを通して「動作するきれいなコード」をつくるための考え方は、エンジニアに限ったものではないと考えるようになりました。

「動作するきれいなコード」をつくるためにまずはユーザーが達成したいこと、いわゆる期待値を設定しその期待値を達成できるコードを書きます。コードを書いたら誰がみてもわかりやすいコードを意識してリファクタリングします。

デザイナーは、デザインをする前にユーザーがどのような状態になって欲しいのか、何を達成したいのかなど、ユーザーのゴールを考えます。ユーザーのゴールを明確にしたら、そのゴールを達成するために必要な課題の解決案や適切なアクションは何かを考えます。解決案からゴールを達成できることがわかったら、今度は細かい部分を修正・改善したり、ゴールまでの道を整えたりして、対象となるユーザーにとってわかりやすく、使いやすいデザインをつくります。

「動作するきれいなコード」をつくるための考え方は、エンジニアリングだけでなく、デザインにも置き換えられると学びました。研修内容はコードを書いたりテストを行ったりと、エンジニアとして求められる知識を学んだように思いますが、「ユーザー視点で考え続ける」という根本は職種関係なく大切にすべきだと知れました。初めて学ぶ考え方や知識に対して、どうすれば自分に紐付けて学びにできるか挑戦する機会になったと思います。

まとめ

今回のワークショップを受けるまで私はコード実装前にテストを書くのは面倒で難しいと感じていました。しかしそれは「実装しながら頭の中でなんとなく目標を考える」ことが癖になっており、最初にちゃんと目標を設定し設計することを疎かにしていたからだと今では思います。ワークショップを経てテストコードの形で目標を明文化し、それに向かって実装していく方が効率的かつ質の高い実装ができることを知り、テストを最初に書くことへの抵抗感はきれいに無くなりました。

このテストファーストの考え方以外にも、リファクタリング技法やペアプログラミングを@t_wadaさんのTDDワークショップで学びました。ワークショップ後はただテストを書くだけでなく、期待する振る舞いは何か、保守性の高いコードとはなにか、どうすれば上手く実装できるかを考え、コードレベルでどう品質を保証するかを意識できるようになったと感じています。

Visionalグループでは新卒採用を積極的に行っています。ご興味がある方は、新卒採用サイトから採用情報をご確認ください。

参考記事

新卒研修① まちづくりワークショップ編

渋谷 樹生
渋谷 樹生

2023年新卒エンジニア。アプリケーション開発とサウナが好きです!