Dトラックでのテスト

ExercismでDの演習をテストする方法を学びましょう


各演習にはユニットテストが用意されています。 実装を書くのは皆さん自身です。 テストのファイルはそれぞれ、テストを実行するコンソール用の実行ファイルを生成します。 テストの実行ファイルを動かすと、失敗したテストごとにメッセージが表示され、テストが失敗したときは終了ステータスが0以外になります。

注意 プロジェクトをビルドするたびに、コードはテストスイートで検証されます。 1つ以上のテストに通らなくても、有効なDのコードであればコンパイルは通ります。

各演習を進める流れは、次のとおりです。

  • dub testでテストを実行します
    • DUBをインストールしない場合は、代わりにdmd source/*.d -de -w -main -unittestを使い、そのあと生成されたバイナリを実行します。
  • それぞれのユニットテストごとに、次のことを行います。
    • コンパイルエラーを解消して、まずテストを失敗させます。
    • テストに通るだけの最小限の実装を書きます。
    • 読みやすさを高めたり重複を減らしたりするために、実装をリファクタリングします。
    • 次のテストを有効にします

注意: Dには言語自体にユニットテストの仕組みがあるので、通常ユニットテストは実装と同じファイルに置きます。 ユニットテストはmainの本体より前に実行され、最終的な実行ファイルでは**-unittest**コンパイラスイッチで有効になります。

ユニットテストの出力を読み取る

すべてのテストが成功すると、テストの出力は次のようになります。

$ dub test
             Generating test runner configuration 'dnd-character-test-library' for 'library' (library).
    Starting Performing "unittest" build using dmd for x86_64.
    Building dnd-character ~master: building configuration [dnd-character-test-library]
     Linking dnd-character-test-library
     Running dnd-character-test-library
1 modules passed unittests

テストが失敗しているときは、出力が読みにくくなることがあります。

$ dub test
             Generating test runner configuration 'dnd-character-test-library' for 'library' (library).
    Starting Performing "unittest" build using dmd for x86_64.
    Building dnd-character ~master: building configuration [dnd-character-test-library]
     Linking dnd-character-test-library
     Running dnd-character-test-library
core.exception.AssertError@source/dnd_character.d(102): unittest failure
----------------
??:? _d_unittestp [0x5568ff91bf05]
source/dnd_character.d:102 void dnd_character.__unittest_L30_C1() [0x5568ff911fee]
??:? void dnd_character.__modtest() [0x5568ff913760]
??:? int core.runtime.runModuleUnitTests().__foreachbody6(object.ModuleInfo*) [0x5568ff928f82]
??:? int object.ModuleInfo.opApply(scope int delegate(object.ModuleInfo*)).__lambda2(immutable(object.ModuleInfo*)) [0x5568ff9148f7]
??:? int rt.minfo.moduleinfos_apply(scope int delegate(immutable(object.ModuleInfo*))).__foreachbody2(ref rt.sections_elf_shared.DSO) [0x5568ff92003f]
??:? int rt.sections_elf_shared.DSO.opApply(scope int delegate(ref rt.sections_elf_shared.DSO)) [0x5568ff9200d1]
??:? int rt.minfo.moduleinfos_apply(scope int delegate(immutable(object.ModuleInfo*))) [0x5568ff91ffcd]
??:? int object.ModuleInfo.opApply(scope int delegate(object.ModuleInfo*)) [0x5568ff9148c9]
??:? runModuleUnitTests [0x5568ff928db7]
??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int function(char[][])*).runAll() [0x5568ff91d248]
??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int function(char[][])*).tryExec(scope void delegate()) [0x5568ff91d1d5]
??:? _d_run_main2 [0x5568ff91d13e]
??:? _d_run_main [0x5568ff91cf27]
/usr/include/dmd/druntime/import/core/internal/entrypoint.d:29 main [0x5568ff90f8f1]
??:? [0x7f473154a249]
??:? __libc_start_main [0x7f473154a304]
??:? _start [0x5568ff90f7e0]
1/1 modules FAILED unittests
Error Program exited with code 1

ここには詳しいスタックトレースがたくさん並んでいますが、重要なのは----------------の上の行です。

core.exception.AssertError@source/dnd_character.d(102): unittest failure

この行は、失敗したアサーションがファイルdnd_character.dの102行目にあることを示しています。 これを使えば、期待どおりに動いていないテストを特定できます。