CodeZine(コードジン)

特集ページ一覧

Goアプリケーションにおけるテスト設計を考える ~Javaとの比較で理解するGoの依存性の分離

  • LINEで送る
  • このエントリーをはてなブックマークに追加

目次

依存性の分離の必要性

 依存性の分離って、なんだか見るからに面倒くさそうですね。どうして単体テストではこんなことをしなければならないのでしょうか。その理由の一つに、既存の振る舞いの破壊やバグの発見が容易になるという点が挙げられます。

 図1のアプリケーションの各ユニットの単体テストを無事書き上げ、テストケースが全て通過することを確認した後、あなたがユニットDに機能追加を行うことになったとします。あなたは無事実装を終えましたが、実はユニットDの既存の振る舞いを意図せず破壊してしまいました。あなたは既存の振る舞いが保持されていることを確認するために先ほど書き上げた単体テストを実行します。すると、図3に示すように、既存の振る舞いを破壊したユニットDだけでなく、それに間接的・直接的に依存しているユニットMainとBのテストまでもが失敗してしまいました。

図3 依存性の分離を行わない場合
図3 依存性の分離を行わない場合

 つまり依存性を分離しないと、本当に壊れたユニットのテスト以外にも、複数のユニットのテストが巻き添えを食らって失敗してしまう可能性が高いのです。これでは、テストレポートをひと目見ただけでは、何が原因でテストが失敗したのか分かりませんね。この例のような単純なアプリケーションでは、デバッガを起動して確認すれば、ものの数分で原因を発見できるでしょうが、今は複雑性の時代です。この例の何十倍にも何百倍にも膨れあがったアプリケーションを扱うのが普通ですよね。そのような場合、一つのユニットの振る舞いを破壊するだけで、何十、何百ものテストが失敗しかねません。その中から本当の原因を見つけ出すのは至難の業です。

 もし依存性が分離されていれば、ユニットMainのテスト時にも、ユニットBのテスト時にも、ユニットDのコードは実行されないため、失敗するのはユニットDのテストだけです。あなたが日頃扱っているアプリケーションの複雑性を考えれば、これが如何に大きな生産性の違いをもたらすかお分かりいただけると思います。

 他にも、外部システムに依存しているコードを実行せずに済むという利点もあります。外部システムとは、ローカルのファイルシステムやリモートにあるデータストア、HTTP APIなどのことで、一般にアクセスが低速で副作用を伴います。外部システムに依存しているコードを単体テストに含めてしまうと、実行が遅くなり、テストの再現性の担保も(副作用の伴う操作が多いことから)難しくなってしまいます。

 もちろん、依存性を分離しないテストも重要です。そのようなテストは結合テストや統合テスト(integration testing)と呼ばれ、単体テストとは区別されています。この記事では単体テストに集中し、結合テストのトピックは扱いません。

依存性を分離するために

 依存性の分離の必要性を説明したところで、それをどのように実現すればよいかを解説しましょう。依存性の分離には、以下の3つの要素が必要です。

  • 多態性(polymorphism)
  • 依存性の注入(dependency injection、 DI)
  • モック実装の自動生成

 ここでいう多態性とは、正確には多態性の一種である部分型付け(subtyping)と呼ばれるもので、複数の型の値をそれらに共通する上位型の変数で扱える機能のことです。部分型付けによって、本番時の依存性とモック実装を同じ型の変数で扱うことができます。部分型付けがなければ、本番時とテスト時で条件分岐したコードを書かなくてはいけませんね。そんなことをしたらテストになりません! 依存性は本番時と異なっても良いのですが、テスト対象は本番時と同じコードを実行しなくてはいけません。

 依存性の注入とは、あるユニットが依存するユニットを、そのユニットの外部から指定できるようにするテクニックのことです。依存性を注入できるようにしておくことで「本番時はこの依存性を使うけど、テスト時はモックを使ってね」ということが実現できるようになるのです。もちろん、上述したように、本番時の依存性とモック実装は、共通の変数で扱う必要があります。

 最後の要素であるモック実装の自動生成は、なくても良いのですが、これがあるとテストコードを書くときの生産性が格段に上がります。自動生成されたモック実装は一般に、メソッドや関数呼び出し時の返り値を設定したり、呼び出し時に例外を投げるようにしたり、依存性が正しい引数で呼び出されたか検証したりする機能が実装されています。これらの機能を活用することで、あるユニットがその依存性を正しく扱っていることを検証できます。これを一つひとつの依存性について自力で実装するのは相当骨が折れます。正しいテストコードを書く敷居が高いと、みんな適当にテストを書いたり、あるいは全くテストを書かなくなったりします。モック実装の生成は、必須でないながらも重要な役割を果たすのです。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5