Chatwork Creator's Note

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

読者になる

UnitTestと対象コードを同じディレクトリに置いたらよかった話

@kyo_agoです。

最近UnitTestと対象コードを同じディレクトリに置いたらよかったので共有したいと思います。
(ちなみにJestでは標準的に採用されている形式ではあります)

具体的には

以下のようなファイル構成でUnitTestを追加する場合、

.
└── source
    └── domain
        └── Message
            └── Lifecycle
                └── MessageRepository.ts

/test/を作成し以下のような構成でテストファイルを置くことが多いと思います。

.
├── source
│   └── domain
│       └── Message
│           └── Lifecycle
│               └── MessageRepository.ts
└── test
    └── domain
        └── Message
            └── Lifecycle
                └── MessageRepository.spec.ts

ただ、この構成だとテストファイルと対象ファイルが離れており、行き来が大変なため、

.
└── source
    └── domain
        └── Message
            └── Lifecycle
                ├── MessageRepository.spec.ts
                └── MessageRepository.ts

上記のようにUnitTestファイルと対象ファイルを同じ場所へ設置するようにしました。

利点

この形式を採用したことで以下のような利点がありました。

UnitTestと対象ファイルの行き来が楽

ディレクトリツリー的にすぐ横にあるのでUnitTestと対象ファイルの行き来が非常に楽です。

これに関しては/test/に置く方式でもIDEで飛ぶようにはできますが、やはりファイル一覧からすぐ遷移できるのは楽です。

対象ファイルにUnitTestが書かれているかどうかすぐに分かる

同じファイル名.spec.tsが存在しない場合UnitTestが書かれていないとすぐ判断できます。

これに関してもカバレッジを取ってIDE上で表示させる方法もありますが、やはりディレクトリツリー的に直ぐ側にあるほうがすぐわかります。

UnitTestから対象ファイルをrequireするパスが長くならない

UnitTestから対象ファイルを読み込む時以下のように書けます。

import {MessageRepository} from './MessageRepository';

上で上げた例の場合、/test/形式だと以下のように書く必要があります。

import {MessageRepository} from '../../../../source/domain/Message/Lifecycle/MessageRepository';

もちろん「テスト実行時にrequire pathを動的に書き換える」、「IDEで補完する」といった解決方法もあります。

しかし、それぞれ「IDEの補完が効かなくなる」、「対象コードからUnitTestに飛ぶためには対象コードへUnitTestへの参照を埋め込む必要がある」といった問題があります。

PullRequestで複数ファイル修正してもUnitTestと対象ファイルの対応が取れる

PullRequestで対象ファイルの修正とUnitTestの修正が上下に並ぶため、修正の対応が取れているかどうかがわかりやすくなります。

/test/形式で複数ファイルを修正したPullRequestの場合、対象ファイルの修正とUnitTestの修正が離れて表示されるため修正箇所に対するUnitTestが正しいかの判断が難しくなります。

UnitTestの対象を明示できる

/test/以下に置く形式ではテストファイル名は比較的自由にできます。

しかし、同じ場所に置く形式の場合自由なファイル名をつけると非常に混乱するため、実質対象ファイル.(spec|test).tsといった形式に固定化されます。

これは自由度が減るという意味ではありますが、複雑なUnitTestを書くことに対する抑止になるため利点であると考えています。

欠点

この形式で感じている欠点は以下です。

実装ディレクトリに余計なファイルが増える

各ディレクトリ内のファイル数が単純に倍になるため、ファイル数は多く感じます。

テストコマンドが複雑になる

テストランナーにもよりますが、mochaの場合標準では/test/以下のファイルを実行するように設計されているため、引数に**/*.spec.jsのような形式を渡す必要があります。

デプロイコマンドが煩雑になる

デプロイ対象と非デプロイ対象が同じディレクトリ内にあるため、依存関係を辿ったbuildが必要になります。

これはwebpackやbrowserifyを使用していれば問題になりませんが、不安であればdependency-cruiserを使うといった解決方法もあります。

注意

この方式はUnitTestのみに採用しているため、E2Eテストやmock等は/test/以下に設置しています。