今回は、『neue cc - Rx + MolesによるC#での次世代非同期モックテスト考察』で解説されているような、イベントベースの非同期パターンを採用するクラスに対して Microsoft Research Moles でモック化するサンプルを Prig(と Moq)に移行してみましょう。前述のページをキーワード「ShowGoogle」で検索すると、例を見つけられると思います。
以下の記事、ライブラリを使用/参考にさせていただいています。この場を借りてお礼申し上げます m(_ _)m
James Dibble - Blog - Microsoft Fakes Part Two
Code rant: How To Add Images To A GitHub Wiki
トンネルの思い出 - 東広島市自然研究会
IronRuby.net
Can we use fakes to test AutomationElement methods
Which Isolation frameworks have you used in the past 3 months?
Battle of the mocking frameworks by @dhelper #mockingframework #softwaredevelopment
djUnit
unit testing - HOW to use MS Fakes shims with NSubstitute mocks? - Stack Overflow
目次
移行サンプル:Microsoft Research Moles による非同期パターンのモック化
プロジェクト MolesMigrationDemo に、メソッド ShowGoogle を持つ ULWebClient を作成し、そのテストプロジェクト MolesMigrationDemoTest を作成します:

This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Net; | |
namespace MolesMigrationDemo | |
{ | |
public class ULWebClient | |
{ | |
public static void ShowGoogle() | |
{ | |
var client = new WebClient(); | |
client.DownloadStringCompleted += (sender, e) => | |
{ | |
Console.WriteLine(e.Result); | |
}; | |
client.DownloadStringAsync(new Uri("http://google.co.jp/")); | |
} | |
} | |
} |
MolesMigrationDemoTest に NUnit への参照を追加し、README.md に従って Prig をインストールします。あ、Moq の追加を忘れずに:

mscorlib に属す Console、System に属す WebClient、DownloadStringCompletedEventArgs のモック化を有効にする必要がありますので、以下のコマンドを実行し、間接スタブ設定を作成していきましょう:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
PM> padd -as "mscorlib, Version=4.0.0.0" | |
PM> padd -as "System, Version=4.0.0.0" |
padd -as は、Add-PrigAssembly -Assembly のエイリアスです。このコマンドの実行により、mscorlib.v4.0.30319.v4.0.0.0.prig と System.v4.0.30319.v4.0.0.0.prig がテストプロジェクトに追加されます:

次に、以下のコマンドを実行します。これは、Console のための間接スタブ設定をクリップボードにコピーするという意味ですね。で、mscorlib.v4.0.30319.v4.0.0.0.prig に貼り付けます:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
PM> pfind ([System.Console]) 'WriteLine\(System.String\)' | pget | clip |
ちなみに、エイリアスについて説明しておくと、pfind は Find-IndirectionTarget、pget は Get-IndirectionStubSetting に当たります。貼り付けた結果はこんな感じ:

同様に、WebClient と DownloadStringCompletedEventArgs の設定を作成します。Get-IndirectionStubSetting は MethodBase の配列を渡しさえすれば、良い感じにスタブ設定を作成してくれますので、PowerShell で、標準のリフレクション API から取得した結果を使い、フィルタリングしても何ら問題はありません:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
PM> $targets = [System.Net.WebClient].GetMembers([System.Reflection.BindingFlags]'Public, Instance') | ? { $_ -is [System.Reflection.MethodBase] } | ? { $_.ToString() -match 'DownloadString' } | |
PM> $targets += [System.Net.DownloadStringCompletedEventArgs].GetMembers([System.Reflection.BindingFlags]'Public, Instance') | ? { $_ -is [System.Reflection.MethodBase] } | ? { $_.ToString() -match 'Result' } | |
PM> $targets | pget | clip |
そうしたら、次のように System.v4.0.30319.v4.0.0.0.prig へ結果を貼り付けます:

さて、ビルドは通りましたか?そうであれば、間接スタブを使うことができます。以下のように、オリジナルの例から移行することができるでしょう:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using MolesMigrationDemo; | |
using Moq; | |
using NUnit.Framework; | |
using System.Net; | |
using System.Net.Prig; | |
using System.Prig; | |
using Urasandesu.Prig.Framework; | |
namespace MolesMigrationDemoTest | |
{ | |
[TestFixture] | |
public class ULWebClientTest | |
{ | |
[Test] | |
public void ShowGoogle_should_write_response_from_google_to_standard_output() | |
{ | |
// Prig には、HostType("Moles") のような属性はありません。 using (new IndirectionsContext()) を代わりに使ってください。 | |
using (new IndirectionsContext()) | |
{ | |
// Arrange | |
var handler = default(DownloadStringCompletedEventHandler); | |
handler = (sender, e) => { }; | |
// 全てのインスタンスのメンバーをモック化する Moles の AllInstances のような機能はありません。デフォルトがそのような機能であるためです。 | |
PWebClient.AddDownloadStringCompletedDownloadStringCompletedEventHandler().Body = (@this, value) => handler += value; | |
PWebClient.RemoveDownloadStringCompletedDownloadStringCompletedEventHandler().Body = (@this, value) => handler -= value; | |
PWebClient.DownloadStringAsyncUri().Body = (@this, address) => | |
{ | |
// 特定のインスタンスに対してモック化を行いたい場合は、PProxy で始まるスタブを使います。 | |
var e = new PProxyDownloadStringCompletedEventArgs(); | |
e.ResultGet().Body = @this_ => "google!modoki"; | |
handler(@this, e); | |
}; | |
var mockWriteLine = new Mock<IndirectionAction<string>>(); | |
mockWriteLine.Setup(_ => _(It.IsAny<string>())); | |
PConsole.WriteLineString().Body = mockWriteLine.Object; | |
// Act | |
ULWebClient.ShowGoogle(); | |
// Assert | |
mockWriteLine.Verify(_ => _("google!modoki"), Times.Once()); | |
} | |
} | |
} | |
} |
さっくりとまとめ
去年の時点では、まだ私の観測範囲でもバリバリ使われてた感じの Moles。 これのインパクトがあったせいか、.NET で static/final/sealed/拡張メソッド入れ替え可能な Mocking フレームワーク、無償なヤツって無いの?、みたいな話題は、QA サイトに定期的に上がる感じ。残念ながら、今のところ、あまり進展は無い感じですね。
V1.0.0 にもなったことですし、今後は私のほうでも、そのような話題を見つけたら、積極的にアプローチしてみたいと思います。人ばs…改善のアイディアも、頂けるかもしれませんしね。
Prig. It is my weekend project but it is expressed an open source alternative to Microsoft Fakes!! ;)
0 件のコメント:
コメントを投稿