このエントリーをはてなブックマークに追加

こんにちは!下田です。

8/7に開催されましたSymfony Meetup Kansai #2 に参加し、複合ユニーク制約についてLTをさせて頂きました!

資料はこちらです。

ちょうど業務で複合ユニーク制約の使い方について調べる機会があり、なんとなく混同していた UniqueEntityUniqueConstraint についてまとめた内容です。

また、当日はセッション2枠、LTが4枠と、発表祭りとなりました。

当日の様子はこちら https://togetter.com/li/1385675

会のはじめの方に主催者の方から「初心者でも気軽に登壇できる場にしていきたい」との表明がありましたが、個人的には既にとても登壇しやすい雰囲気の会になっていたと思います。

どういうわけかFormについての話題が多く「確かにみんな一度は悩むよな…」とひとり静かに共感しておりました。

普段和歌山県にいる僕のようなリモートワーカーにとって、このように関西でSymfonyについての悩みや知見をオンサイトで共有できる場ができるのはとてもありがたく、ぜひまた参加させていただきたいと思いました!

関西からもSymfonyを盛り上げていきましょう!! :muscle:


このエントリーをはてなブックマークに追加

09.おわりに

カオスなテストコードについて悩み始めてから自分なりの答えを見つけるまでに、ほぼ1年近くかかりました。JavaScript は手軽にプログラミングを書く事のできる言語であり、テスティングフレームワークにもテストの目的や手段を強制するようなルールはありません。この自由さがフロントエンドのテストの難しさではないかと思います。

ようやく悩みから解放されたところで、もう一度ふりかえってみたいと思います。

テストって何の目的で書くのか?

間違いなく「自分のため」です。動作確認のためのブラウザのリロード地獄から解放してくれ、半年後に記憶を失っているであろう自分のために書いています。そして自分のためにスッキリ整理整頓したテストコードは、同じチームで開発する仲間やレビュアーのための副産物にもなります。

どうやって書いたらいいのか?

戦略を立ててスタイルを模索してみましょう。今回紹介したのは私が自分のアプリケーションに合うと思ったテストのスタイルで、開発チームやアプリケーションが変わればまた別のスタイルが必要かもしれません。使っているフレームワークやパッケージのテストを時々覗いてみる習慣を付ければ、いろいろなヒントが得られると思います。

まとめ

一時は嫌いになりかけたテストですが、私はやっぱりテストコードを書くのが好きです。機能の完成度が高まる感じ、リリース前にバグを発見した時のお手柄感、最高ですよね。

長いブログにお付き合いいただきありがとうございました。 みなさま、よきテストライフを!


このエントリーをはてなブックマークに追加

08.そして脱却へ

悩んだ末にたどり着いたコードです。

スモークテスト

describe('アイテム検索・登録モーダル画面', () => {
  it('コンポーネント作成', () => {
    const modal = compile(
      '<item-registration-modal data="items"></item-registration-modal>'
    );

    expect(modal).toBeTruthy();
  });
});

コンポーネントがエラーなく作成できる事だけをテストします。 依存サービスや子コンポーネントはできるだけモックでない本物を使いますが 初期処理が足りずにエラーになる場合は、サービスそのものでなくサービスのメソッドなど一部だけをモックします。

ユニットテスト

describe('アイテム検索・登録モーダル画面 リスト表示', () => {

  function setup(items) {
    spyOn(searchService, ['search']);
    searchService.search.and.returnValue(Promise.resolve(items));

    return compile(
      '<item-registration-modal data="items"></item-registration-modal>'
    );
  }

  function assert(items, expected) {
    const modal = setup(items);
    modal.getElementById('searchText').value = 'カルテットコミュニケーションズ';
    modal.getElementById('searchButton').click();

    expect(modal.getElementsByClassName('item').length).toBe(expected.listSize);
    expect(modal.getElementsById('saveButton').attr('disabled') === 'disabled')
      .toBe(expected.disabled);
  }

  it('検索に2件該当', () => {
    const items = [fakeItem(100), fakeItem(101)];
    const expected = { listSize: 2, disabled: false };
    assert(items, expected);
  });

  it('何も該当しない', () => {
    const items = [];
    const expected = { listSize: 0, disabled: true };
    assert(items, expected);
  });
});

export function fakeItem(id) {
  return {
    id: id,
    name: 'fake',
  };
}

依存サービスや子コンポーネントは全てモックを使い、必要なモデルはフェイクを使います。

テストの目的を示した describe() は1ファイルにひとつしか書きません。上記の例だと「リスト表示」だけをテストします。

setup() は環境づくりに徹する

実際のテスト、それもコンポーネントを対象にしたテストの場合、依存サービスのモックや HTML のコンパイルなど事前準備だけで結構な行数を使います。これらを全て setup() にまとめます。

assert() は検証に徹する

ひとつのシナリオを実行し、結果を検証します。検証のための expect() はいくつでも呼び出していいルールとします。

it() はパターン列挙に徹する

前提条件と結果にパターンを持たせます。 assert() を呼び出す以外の事は何もしません。

フォルダ構造

component
├── item-registration-modal
│   ├── component.js
│   ├── component.html
│   ├── smork-test.js
│   └── tests
│         ├── list-test.js
│         ├── resoponse-test.js
│         └── search-test.js

ひとつのコンポーネントに対して、スモークテストがひとつ、ユニットテストが複数という構造にします。

ユニットテストは「リスト表示」「API レスポンスのパターン」「再検索」など小さなシナリオ単位でテストファイルを分けるようにします。

何がいいのか

スモークテスト大事

例えば子コンポーネントに必須パラメータを増やした時、モックでない本物を使ったテストが落ちればコンポーネント側の修正が必要な事に気づけます。逆に個々のユニットテストでは、テストに関係のないパラメータ増加が原因で大量にテストが落ちると修正してまわるのがとても大変です。そのために スモークテストはモックでない本物を使い、ユニットテストはモックを使う というように役割を分けているのです。

仕様の数だけユニットテストを作る

ファイル名にテストの目的を書くことで、後からとても探しやすくなります。またユニットテストが増える事でコンポーネントが抱える仕事の多さが一目瞭然になります。私はファイル数が 3 以上になった時にコンポーネント分割を検討するようにしています。

あちこち検証してもカオスになりにくい

assert() に検証を集める事で、サービスがコールされる事、ボタンが disabled になる事、リストに表示された件数が 2 件になる事、を一箇所に集める事ができます。もし assert() のシナリオや検証にそぐわないイレギュラーなパターンが出てきたら、ユニットテストのファイルを増やせば良いだけです。

異常パターンも列挙できる

it() を仕様の列挙でなく、前提条件と結果のパターンにする事により、正常パターンと異常パターンを混在させる事ができます。

テストコード以外に大事にしているポイント

テストを書くタイミング

私はプロダクションコードをざっくり書き終えてブラウザでの動作確認を一度通したタイミングでユニットテストを書き始めるようにしています。実運用でのコンポーネントは、非同期処理だったり、コンポーネント連携だったり、イベントハンドリングだったり、複雑な処理をしています。その全てを把握しつつテストから書き始めるのはとても難しく、そこは TDD にこだわらず人間の目を通したほうが早くて確実です。

レガシーなテストコードに逆らわない

過去に書いたカオスなテストに新たなコードを追加する場合、あえてそのままカオスなスタイルでコードを書くようにしています。後からコードを追加する時って、その仕様に関連する部分を見つけ出してざーっとコードを読みますよね?その時に全く別のファイルに別のスタイルのコードが書いてあると、とても読みづらく検索性も低くなってしまいます。

テストは7割の力で書く

テストコードを完璧に書かないといけないという思い込みは、時に開発を苦しくさせます。思い出しましょう、全パターン網羅のテストは地球が滅亡する頃に完了します。誰もが見て完璧だと思うテストは tc39 のあのレベルです。画面に青いボタンを追加した時に「背景色が青である事」って検証しないですよね?無意識にそれを無視しているように、必要なポイントで必要なテストが書ければ十分じゃないかと思うのです。体感的には「ほぼ満足」の8割でなく「ちょっと足りないかな」の7割の力で書くテストが、苦しくならない絶妙なバランスです。