Moqは、匿名メソッドと式ツリーに基づいて構築された、シンプルで軽量な分離フレームワークです。 彼はコード生成を使用してムックを作成するため、「ウェット」インターフェース、仮想メソッド(および保護メソッド)を許可し、「ウェット」非仮想および静的メソッドを許可しません。
注市場には、何でも「濡れる」ことができるフレームワークが2つしかありません。 これらはTypeMockIsolatorとMicrosoft Fakesであり、Visual Studio 2012(以前の
Microsoft Moles )で利用できます。 これらのフレームワークは、Moqとは異なり、コード生成を使用しませんが、CLRプロファイリングAPIを使用すると、ほとんどすべてのメソッドに割り込んで、静的メソッド、非仮想メソッド、またはプライベートメソッドでもmoki /スタブを作成できます。
Moqでは、「スタブ」と「モック」の間に分離はありません。より正式には、状態検証と動作検証の間に分離はありません。 また、ほとんどの場合、
スタブとmokaの違いはそれほど重要で
はなく、同じスタブが両方の役割を実行することもありますが、単純なものから複雑なものまで例を検討するため、最初に状態をチェックする例を見てから、動作をチェックします。 。
状態 検証
例として、次のインターフェイスの単体テストを検討します。
public interface ILoggerDependency { string GetCurrentDirectory(); string GetDirectoryByLoggerName(string loggerName); string DefaultLogger { get; } }
1.
GetCurrentDirectoryメソッドのスコープ:
2.
GetDirectoryByLoggerNameという
見出しのメソッドは 、常に同じ結果を返します。
3.引数に応じて結果を返す
GetDirrectoryByLoggerNameメソッドの
スコープ :
4.
DefaultLoggerプロパティの
見出し :
5.「moq機能仕様」(Moq v4で表示)を使用して、1つの式で複数のメソッドの動作を設定します。
6. Setupメソッド(「古い」v3構文)を呼び出して、いくつかのメソッドの動作を設定します。
var stub = new Mock<ILoggerDependency>(); stub.Setup(ld => ld.GetCurrentDirectory()).Returns("D:\\Temp"); stub.Setup(ld => ld.GetDirectoryByLoggerName(It.IsAny<string>())).Returns("C:\\Temp"); stub.SetupGet(ld => ld.DefaultLogger).Returns("DefaultLogger"); ILoggerDependency logger = stub.Object; Assert.That(logger.GetCurrentDirectory(), Is.EqualTo("D:\\Temp")); Assert.That(logger.DefaultLogger, Is.EqualTo("DefaultLogger")); Assert.That(logger.GetDirectoryByLoggerName("CustomLogger"), Is.EqualTo("C:\\Temp"));
注前述したように、Moqではmokaとスタブは分離されていませんが、2つのスタブ初期化構文を区別する方がはるかに簡単です。 そのため、「moq機能仕様」構文は、状態のテスト(つまり、スタブ)にのみ使用でき、動作の設定には使用できません。
Setupメソッドを使用したスタブの初期化は、第1に、より冗長になり、第2に、それを使用するときに、動作または状態を確認するかどうかが完全に明確ではありません。
行動 検証
次のクラスとインターフェイスを使用して、動作をテストします。
public interface ILogWriter { string GetLogger(); void SetLogger(string logger); void Write(string message); } public class Logger { private readonly ILogWriter _logWriter; public Logger(ILogWriter logWriter) { _logWriter = logWriter; } public void WriteLine(string message) { _logWriter.Write(message); } }
1.
Loggerクラスのオブジェクト(引数付き)で
ILogWriter .Writeメソッドの呼び出しを確認します。
var mock = new Mock<ILogWriter>(); var logger = new Logger(mock.Object); logger.WriteLine("Hello, logger!");
2.指定された引数を使用して
ILogWriter .Writeメソッドの呼び出しを確認します。
mock.Verify(lw => lw.Write("Hello, logger!"));
3.
ILogWriter .Writeメソッドが1回だけ
呼び出されたことの検証(これ以上、以下):
mock.Verify(lw => lw.Write(It.IsAny<string>()), Times.Once());
注依存関係が発生する回数を確認するためのオプションは多数あります。 Timesクラスには、AtLeast(int)、AtMost(int)、Exactly、Betweenなどのさまざまなメソッドがあります。
4.
Verifyメソッドを使用した動作の
検証 (いくつかの仮定を確認する必要がある場合に便利です):
var mock = new Mock<ILogWriter>(); mock.Setup(lw => lw.Write(It.IsAny<string>())); var logger = new Logger(mock.Object); logger.WriteLine("Hello, logger!");
5.
Verify ()メソッドを使用して複数の呼び出しをテストします。
場合によっては、複数の
Verifyメソッドを使用して複数の呼び出しをテストするのは不便です。 代わりに、模擬オブジェクトを作成し、
Setupメソッドを使用して予想される動作を設定し、1つの
Verify ()メソッドを呼び出してこれらの前提条件をすべて検証でき
ます 。 このような手法は、テストのSetupメソッドで作成されたモックオブジェクトを再利用するのに便利です。
var mock = new Mock<ILogWriter>(); mock.Setup(lw => lw.Write(It.IsAny<string>())); mock.Setup(lw => lw.SetLogger(It.IsAny<string>())); var logger = new Logger(mock.Object); logger.WriteLine("Hello, logger!"); mock.Verify();
トピックからの逸脱。 厳密な モデルと 緩い モデル
Moqは、
厳格と緩いの2つの動作テストモデルをサポートしています。 デフォルトでは、無料のテストモデルが使用されます。つまり、テストされたクラス(テスト対象クラス、CUT)は、アクションの実行中(Actセクション)、依存関係のメソッドを呼び出すことができ、すべてを指定する必要はありません。
したがって、前の例では、
ロガーの .WriteLineメソッドは
ILogWriterインターフェイスの2つのメソッド、
Writeメソッドと
SetLoggerを呼び出します。
MockBehavior .Strictを使用する場合、どの正確な依存関係メソッドを呼び出すかを明示的に指定しないと、
Verifyメソッドは失敗します。
var mock = new Mock<ILogWriter>(MockBehavior.Strict);
MockRepositoryを使用する
MockRepositoryクラスは、スタブを作成するための別の構文を提供します。最も重要なことは、単一のメソッドを呼び出すことで、複数のモックオブジェクトを保存し、より複雑な動作をチェックできるようにすることです。
1.
MockRepository .Ofを使用してスタブを作成します。
この構文は
Mock .Ofの使用に似ていますが、&&演算子ではなく、いくつかの
Whereメソッドを使用して、さまざまなメソッドの動作を設定できます。
var repository = new MockRepository(MockBehavior.Default); ILoggerDependency logger = repository.Of<ILoggerDependency>() .Where(ld => ld.DefaultLogger == "DefaultLogger") .Where(ld => ld.GetCurrentDirectory() == "D:\\Temp") .Where(ld => ld.GetDirectoryByLoggerName(It.IsAny<string>()) == "C:\\Temp") .First(); Assert.That(logger.GetCurrentDirectory(), Is.EqualTo("D:\\Temp")); Assert.That(logger.DefaultLogger, Is.EqualTo("DefaultLogger")); Assert.That(logger.GetDirectoryByLoggerName("CustomLogger"), Is.EqualTo("C:\\Temp"));
2.
MockRepositoryを使用して、いくつかのモックオブジェクトの動作を設定します。
ILogWriterと
ILogMailerの 2つの依存関係を必要とする、より複雑な
SmartLoggerクラスがあるとし
ます 。 テストクラスは、
Writeメソッドを呼び出すときに、2つの依存関係のメソッドを呼び出す必要があります。
var repo = new MockRepository(MockBehavior.Default); var logWriterMock = repo.Create<ILogWriter>(); logWriterMock.Setup(lw => lw.Write(It.IsAny<string>())); var logMailerMock = repo.Create<ILogMailer>(); logMailerMock.Setup(lm => lm.Send(It.IsAny<MailMessage>())); var smartLogger = new SmartLogger(logWriterMock.Object, logMailerMock.Object); smartLogger.WriteLine("Hello, Logger"); repo.Verify();
その他のテクニック
インターフェイスによってモックオブジェクト自体を取得すると便利な場合があります(
ISomethingインターフェイスを介して
Mock <ISomething >を取得します)。 たとえば、スタブを初期化するための関数構文はモックオブジェクトを返しませんが、すぐに必要なインターフェイスを返します。 これは、いくつかの単純なメソッドをテストするのに便利ですが、動作を確認する必要がある場合や、パラメーターごとに異なる結果を返すメソッドを設定する場合は不便です。 そのため、メソッドの一部でLINQベースの構文を使用し、他の部分で
Setupメソッドを使用すると便利な場合があり
ます 。
ILoggerDependency logger = Mock.Of<ILoggerDependency>( ld => ld.GetCurrentDirectory() == "D:\\Temp" && ld.DefaultLogger == "DefaultLogger");
さらに、Moqを使用すると、保護されたメソッドの動作を確認したり、イベントをテストしたり、その他の機能を使用したりできます。
サイトリンク
Githubの例モキとスタブマイクロソフトのほくろ