
フルタイムの開発者が単体テストを計画している場合、この悪名高い「ユニット」が何を意味するのかという問題はめったに発生しません。ほとんどの場合、ユニットはクラスであり、したがって2つ以上のクラスを使用するテストはユニットテストですない-これは統合テストです。 ここでは、もちろん、フレームワークまたはサードパーティライブラリのクラスへのバインドは非常に正常であり、テストする必要はありませんので
、クラスについて話します(どうすればいいのか...)。
したがって、問題が発生しました:クラスをテストする方法ですが、使用する他のクラスの代わりに、それ自体は何もしない(たとえば、データベースにデータを書き込まない)オブジェクトを配置しますが、期待される値を返すか、例外をスローしますか? この問題の解決策は、これらのトリッキーなダミーオブジェクトまたは「モカ」(英語の
モック -コピー、模倣)の作成に役立つモックフレームワークによって提供されます。
TypeMockライブラリを使用して、これらの「moka」が実際にどのように機能するかを見てみましょう。
mokを使用するには何が必要ですか? 3つのこと-Visual Studio、TypeMock、および適切な単体テストフレームワーク(MbUnitを使用します)。 まあ、これをすべて勉強したいという願望。
最初のステップ
最初に小さなスクリプト。 私が従業員にどれだけ借りているかを計算する仕組み会計システムがあるとしましょう。 次のようになります。
公共階級の労働者
{
プライベートリスト<int> workHours {get; セット; }
public int GetTotalHoursWorked(){return workHours.Sum(); }
}
パブリッククラスの給与
{
public int CalculatePay(ワーカーワーカー)
{
//全員に1時間に10ドルを支払います(私は悪です)
return worker.GetTotalHoursWorked()* 10;
}
}
特定のクラスWorkerがあり、特定の量の作業を生成します。このクロックは配列に追加されます。 給与計算システム-給与-は、これらの時間の量を受け取り、時間料金で乗算し、支払いを行います。
Payroll
クラスをテストしたいが、実際の単体テストにするには、たとえば
GetTotalHoursWorked()
関数が
まったく呼び出されないように、依存する
Person
クラスを分離する必要があると想像してください。 どうやってやるの? 非常に簡単:最初に、通常どおり
Payroll
を作成しますが、
Person
ではなく、モックオブジェクトを作成します。
ワーカーw = Isolate.Fake.Instance <Worker>();
給与計算p =新しい給与計算();
これで、従業員は従業員のようになりましたが、特定の設定可能な充填があります[
1 ]。 ここで、カウントを実行する必要がありますが、
実際の Worker
は影響を受けません。 これを行うには、
GetTotalHoursWorked()
呼び出しに
GetTotalHoursWorked()
必要があります。 方法は次のとおりです。
Isolate.WhenCalled(()=> w.GetTotalHoursWorked())。WillReturn(40);
すべてが単純です
Person.GetTotalHoursWorked()
への後続の呼び出しの代わりに、40
Person.GetTotalHoursWorked()
数字は
Person.GetTotalHoursWorked()
なものになります。
ここで行ったのは、TypeMockがサポートするArrange-Act-Assert(AAA)方法論の3つのフェーズの最初であるArrangeフェーズです[
2 ]。 次に、第2段階であるActを見てみましょう。 ここでは、実際にメソッドを呼び出します。つまり、テスト対象のシステムでアクションを実行します。
int result = p.CalculatePay(w);
今、結果を推測してみてください! 結局、
workHours
初期化することさえしません
workHours
-値は
null
です。 それにもかかわらず、400という非常に現実的な結果が得られます。さらに、
GetTotalHoursWorked()
メソッドが実際に呼び出されたことを確認することもでき
GetTotalHoursWorked()
置換されたメソッドが呼び出されたという事実は重要ではありません)。 これがAAAの最後のフェーズ、つまりアサートです。 私たちは見ます:
Assert.AreEqual(400、結果);
Isolate.Verify.WasCalledWithAnyArguments(()=> w.GetTotalHoursWorked());
最後に、テスト全体を見てみましょう[
3 ]
[テスト]
public void TestPayroll()
{
//アレンジ
給与計算p =新しい給与計算();
ワーカーw = Isolate.Fake.Instance <Worker>();
Isolate.WhenCalled(()=> w.GetTotalHoursWorked())。WillReturn(40);
//行動する
int result = p.CalculatePay(w);
//アサート
Assert.AreEqual(400、結果);
Isolate.Verify.WasCalledWithAnyArguments(()=> w.GetTotalHoursWorked());
}
それで、私たちは何をしましたか?
Payroll.CalculatePay()
メソッドをテストし、
Person
パラメーターを、予測どおりに動作し、クラスの実際のプロパティとメソッドに影響を与えないように変更しました。
非公開
最初の例では、すべてが非常にシンプルでした。すべての要素がパブリックであったため、アクセスに問題はありませんでした。 ここで、
Worker.GetTotalHoursWorked()
メソッドが別のアセンブリにあり、
internal
としてマークされていることを想像して
Worker.GetTotalHoursWorked()
:
公共階級の労働者
{
プライベートリスト<int> workHours {get; セット; }
内部int GetTotalHoursWorked(){return workHours.Sum(); }
}
ヒューストン、問題があります! テストはコンパイルされなくなりました。
GetTotalHoursWorked()
を使用する2行のコードにはアクセスできなくなりました。
//動作しません
Isolate.WhenCalled(()=> w.GetTotalHoursWorked())。WillReturn(40);
//そしてこれも
Isolate.Verify.WasCalledWithAnyArguments(()=> w.GetTotalHoursWorked());
非公開メソッドを置き換える方法は? 小学生、ワトソン!
Isolator.NonPublic
を使用して、名前でメソッドを定義できます。
Isolate.NonPublic.WhenCalled(w、 "GetTotalHoursWorked")。WillReturn(40);
...
Isolate.Verify.NonPublic.WasCalled(w、 "GetTotalHoursWorked");
以上です! メソッドと同様に、たとえばプロパティまたはインデクサーへの呼び出しをインターセプトできます(
operator this[]
)。 さて、それに応じて呼び出しチェックを行うことができます。
統計、使用率など
TypeMockは、
new
演算子で作成できるオブジェクトの操作に加えて、静的オブジェクトの操作方法も知っています。 たとえば、静的コンストラクターを偽造するには、
Isolate.Fake.StaticConstructor(typeof (T));
呼び出すだけ
Isolate.Fake.StaticConstructor(typeof (T));
、その後、以前と同様にTypeMockを使用します。 静的メソッドでも同じことが行われます。
TypeMockは、オブジェクトをmokasに置き換えるだけでなく、アヒルのタイピングをサポートしています。つまり、まったく同じインターフェースを持たない場合でも、あるオブジェクトを別のオブジェクトに置き換える機能です。 以下に小さな例を示します。
パブリッククラスの犬
{
公共の犬(){}
パブリック文字列MakeSound()
{
return "Woof";
}
}
パブリッククラスのアヒル
{
パブリック文字列MakeSound()
{
return "Quack";
}
}
ここにはアヒルと犬がいます。そして当然、犬にからかいます。 TypeMockでは、これは次のように行われます。
[テストフィクスチャ、分離]
パブリッククラステスト
{
[テスト]
パブリックボイドテスト()
{
//犬を偽造する
Dog dog = Isolate.Fake.Instance <Dog>();
ダックダック=新しいダック();
//犬の呼び出しをアヒルの呼び出しに置き換えます
Isolate.Swap.CallsOn(犬).WithCallsTo(アヒル);
//犬をからかう
文字列の音= dog.MakeSound();
//やった?
Assert.AreEqual( "Quack"、サウンド);
Isolate.Verify.WasCalledWithAnyArguments(()=> dog.MakeSound());
}
}
以上です!
この短い投稿で、mokiはまったく怖くなく、使いやすいことを示しました。 ご清聴ありがとうございました!
注釈
- ↑これがすべて機能するためには、GACから「TypeMock Isolator」と「TypeMock Isolator-Arrange-Act-Assert」の2つのアセンブリへのリンクをアセンブリに追加する必要があります。
- ↑他の2つのアプローチ-Reflective MocksとNatural Mocksは、このエッセイでは考慮されていません。
- ↑また、
Test
属性に加えて、 Isolated
属性をメソッドレベルまたはクラスレベルで使用する必要があることに注意してください。この属性を使用すると、使用されているモックオブジェクトのコンテキストをクリアできます。
