.NET Tools

テスト駆動開発ツール最前線(後編)

− Mockオブジェクトを使ったNUnit 2.2によるテスト −

(株)ピーデー 川俣 晶
2004/11/17
Page1 Page2 Page3

より高度なMockオブジェクトを実現するNMock

 NUnitが提供するMockオブジェクトは、限定された機能を持つ軽量版である。より本格的なMockオブジェクトを提供するソフトウェアとして、NUnitのドキュメントの中ではNMockの名前が示唆されている。そこで、ここではNMockを使った簡単なサンプル・コードも1つ見てみよう。NMockの特徴の1つは、期待される引数値の指定方法の柔軟性である。そこでそれを見るために、3つの値のうち、どれが引数に渡されるか予測できないような例を記述してみた。

 このサンプル・コードは、VS.NETでVB.NETのクラス・ライブラリのプロジェクトを作成した後、nunit.framework.dllとNMockの本体であるnmock.dllへの参照を追加し、以下のコードを入力することでテスト可能となる。

Imports NUnit.Framework
Imports NMock
Imports NMock.Constraints

Namespace TestTarget1
  Public Interface IJankenJudgeMan
    Sub SetNextHand(ByVal hand As String)
  End Interface

  ' テスト対象となるクラス
  Public Class JankenPlayer
    Public jankenJudgeMan As IJankenJudgeMan

    Public Sub New(ByVal jankenJudgeMan As IJankenJudgeMan)
      Me.jankenJudgeMan = jankenJudgeMan
    End Sub

    Private randomGenerator As New Random(DateTime.Now.Second)
    Public Sub PlayIt()
      Select Case randomGenerator.Next(2)
        Case 0
          jankenJudgeMan.SetNextHand("グー")
        Case 1
          jankenJudgeMan.SetNextHand("チョキ")
        Case 2
          jankenJudgeMan.SetNextHand("パー")
      End Select
    End Sub
  End Class
End Namespace

Namespace Test1
  ' テストのためのクラス
  <TestFixture()> _
  Public Class TestJankenPlayer
    Private mock As mock
    Private player As TestTarget1.JankenPlayer

    <SetUp()> _
    Public Sub SetUp()
      mock = New DynamicMock(GetType(TestTarget1.IJankenJudgeMan))
      player = New TestTarget1.JankenPlayer(mock.MockInstance)

      mock.Expect("SetNextHand", New IsIn("グー", "チョキ", "パー"))
    End Sub

    <Test()> _
    Public Sub TestPlayIt1()
      player.PlayIt()
      Mock.Verify()
    End Sub
  End Class
End Namespace
NMockのMockオブジェクトを利用したVB.NETのサンプル・コード

 基本的なテストの構造はNUnitのMockオブジェクトと同じことが分かるだろう。Expectメソッドによって、期待されるメソッド呼び出しを指定することも、よく似ている。しかし、Expectメソッド呼び出しの引数については、かなり雰囲気が異なっている。引数として渡すことが期待される値の代わりに、IsInクラス(NMock.Constraints.IsIn)のインスタンスを渡している。

mock.Expect("SetNextHand", New IsIn("グー", "チョキ", "パー"))

 このIsInクラスは、コンストラクタの引数で指定された値のいずれかがメソッドの引数に渡されることを示している。この場合は、"グー"、"チョキ"、"パー"のいずれかの文字列が引数に渡されるという意図が示されている。このサンプル・コードでは、乱数によってどの文字列を渡すかが決定されるため、3つの値のうちの1つを限定できない。しかし、3つのうちのどれか1つ、という指定が可能になることにより、テストが可能になる。もちろん、この3つ以外の文字列が渡されれば、テスト失敗となる。実際には引数の値が乱数で決まるようなことは、ゲームを開発しているのでなければ、さほど多くはないだろう。しかし、実行するタイミングによって引数の値が変化するメソッドを記述するケースは考えられるので、有益な機能といえるだろう。

 NMockには、IsInクラスのほかに、どんな値でもよいIsAnythingクラスや、大文字・小文字を無視した一致を判定するIsEqualIgnoreCaseメソッドなどもあり、より高度なテストを記述するには役に立ちそうである。

まとめ:これからテストはどこに向かうのか

 Javaを使う場合のMockオブジェクトと、.NET Framework上のMockオブジェクトには質的な相違がある、と筆者は感じる。その相違は、Mockオブジェクトの作りやすさの相違である。Javaは、メソッドがデフォルトでvirtual(仮想メソッド)扱いとなっている。そのため、Mockオブジェクトにしたいクラスを継承することで、メソッドの動作を容易に入れ替えることができる。例えば、データベースに書き込むメソッドを、定数値を返すだけのメソッドに入れ替えることも容易である。それゆえに、NUnitと異なり、Java用のテスト・フレームワークであるJUnitには、今回紹介したようなMockオブジェクト生成機能は標準で含まれていないのだろう(JUnit用の外部ソフトとしては多数存在するようであるが)。

 しかし、.NET Framework上の主要なプログラム言語(C#やVisual Basic .NET)では、メソッドのデフォルトがvirtualではない。つまり、継承して機能を入れ替えることができない。そのため、Javaのように継承するだけでMockオブジェクトを作ることができない。もちろん、デフォルトがvirtualであるということは、プログラマに強力すぎる拡張性を持たせることにつながるため、その方がよいとも断言できない。むしろ、筆者はJavaのようにデフォルトがvirtualであることは、害の方が多いと考えている。だが、Mockオブジェクトの問題に限っていえば、Javaの方が作りやすかったのは事実だと思う。

 今回見てきたNUnitやNMockでは、すでに確固たるポリシーを持って、独自の道を歩き出している様子がうかがえる。例えば、簡単に継承できないことを嘆くよりも、積極的にインターフェイスを定義してしまえ、というような主張があるように筆者には感じられる。つまり、既存のクラスを継承してMockオブジェクトを作るのではなく、Mockオブジェクトに対応するインターフェイスを定義し、それに対して動的なMockオブジェクトを作成せよ、ということである。

 それは本来不用のインターフェイスを別途定義することにつながり、ソース・コードの少々の複雑化や手間の増加につながる。しかし、それらの問題を補って有り余るメリットが、インターフェイスの定義によって得られる、と考えることも間違いではないだろう。つまり、インターフェイスを介することによって、クラス間の依存性が減少するのである。

 ここで気になることは、テストの都合でソース・コードの構造を変えてしまうことが、果たして正しいかどうかである。テストとはプログラムの品質を調べるものであるのに、それがプログラムの構造にまで影響を与えることは、僭越(せんえつ)で筋が通らないことのようにも思える。

 しかし、ここではあえて、「変えてよい。むしろ変えるべきである」と断言しておこう。ソース・コードは、ただ単にソース・コードだけで存在しているわけではない。テストも含め、周辺のさまざまな事情と密接に連携しながらそこに存在しているのである。もし、少しだけソース・コードがテストの都合に妥協するだけで、全体の質がより向上するのであれば、それは行うべき行為といえるだろう。End of Article

 

 INDEX
  [.NET Tools]
  テスト駆動開発ツール最前線(前編)
     1.プログラム開発の効率をアップするための方法
     2.GUIテストランナーとテストのための新機能(1)
     3.GUIテストランナーとテストのための新機能(2)
  テスト駆動開発ツール最前線(後編)
     1.Test Driven .NETによる統合されたテスト
     2.Mockオブジェクトとは何か− NUnitのMockオブジェクト
   3.より高度なMockオブジェクトを実現するNMock

インデックス・ページヘ  「.NET Tools」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間