2014年12月6日土曜日

移行サンプル:Typemock Isolator による MessageBox を使うテストのモック化 - from "Prig: Open Source Alternative to Microsoft Fakes" Wiki -

まとまってきたドキュメントを日本語の記事にもしておくよシリーズ第 8 段!(元ネタ:Prigwiki より、MIGRATION: Test using MessageBox by Typemock Isolator。同シリーズの他記事:1 2 3 4 5 6 7

このシリーズも残り僅か。今回もよろしくお願いします。Typemock は、Isolator の Quick Start で、MessageBox をモックに入れ替えるサンプルを紹介しています。これも Prig(と Moq)に移行することが可能です。


以下の記事、ライブラリを使用/参考にさせていただいています。この場を借りてお礼申し上げます m(_ _)m
Testing code that rely on Microsoft Azure Management Libraries using Microsoft Fakes
c# - The type is defined in an assembly that is not referenced, how to find the cause - Stack Overflow
.net - Mircosoft fakes - shims without ShimsContext - Stack Overflow
Unit Test for ShimDataTableCollection Count
c# - How to know if a MemberInfo is an explicit implementation of a property - Stack Overflow
#5816 (any_range requires copyable elements) – Boost C++ Libraries
#10360 (Since 1.56, any_range use static cast of reference instead of implicit conversion) – Boost C++ Libraries
#10493 (Since 1.56, any_range with non-reference references can cause UB) – Boost C++ Libraries
hunting bugs with git bisect and submodules - Least Significant Bit
AdventCalendar - git bisect で問題箇所を特定する - Qiita
便利!電動歯ブラシ | Boost.勉強会 #16 大阪





目次

準備
まずは、間接設定を作成する必要があります。Package Manager Console を開き、Default project: をテストプロジェクトに変更してください。その後、以下のコマンドを実行します:
PM> dir
Directory: C:\users\akira\documents\visual studio 2013\Projects\IsolatorMigrationDemo
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2014/10/10 9:31 IsolatorMigrationDemo
d---- 2014/10/10 9:37 IsolatorMigrationDemoTest
d---- 2014/10/10 9:37 packages
-a--- 2014/10/10 9:32 1561 IsolatorMigrationDemo.sln
PM> cd .\IsolatorMigrationDemo\bin\Debug
PM> dir
Directory: C:\users\akira\documents\visual studio 2013\Projects\IsolatorMigrationDemo\IsolatorMigrationDemo\bin\Debug
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2014/10/10 9:31 7680 IsolatorMigrationDemo.exe
-a--- 2014/10/10 9:30 189 IsolatorMigrationDemo.exe.config
-a--- 2014/10/10 9:31 28160 IsolatorMigrationDemo.pdb
-a--- 2014/10/10 9:30 23168 IsolatorMigrationDemo.vshost.exe
-a--- 2014/10/10 9:30 189 IsolatorMigrationDemo.vshost.exe.config
-a--- 2013/06/18 21:28 490 IsolatorMigrationDemo.vshost.exe.manifest
PM> padd -af (dir .\IsolatorMigrationDemo.exe).FullName
PM> padd -as "System.Windows.Forms, Version=4.0.0.0"
PM>
view raw 08_01.cs hosted with ❤ by GitHub

次に、Isolator のサンプルで使用しているメソッドのための間接設定を取得しましょう。PowerShell(コンソール)を開き、情報を取得するために以下のコマンドを実行します:
PS> $pwd
Path
----
C:\Users\Akira\Documents\Visual Studio 2013\Projects\IsolatorMigrationDemo\IsolatorMigrationDemo\bin\Debug
PS> powershell
Windows PowerShell
Copyright (C) 2013 Microsoft Corporation. All rights reserved.
PS> ipmo "C:\Users\Akira\Documents\Visual Studio 2013\Projects\IsolatorMigrationDemo\packages\Prig.1.0.0\tools\Urasandesu.Prig"
PS> dir
Directory: C:\Users\Akira\Documents\Visual Studio 2013\Projects\IsolatorMigrationDemo\IsolatorMigrationDemo\bin\Debug
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2014/10/10 9:31 7680 IsolatorMigrationDemo.exe
-a--- 2014/10/10 9:30 189 IsolatorMigrationDemo.exe.config
-a--- 2014/10/10 9:31 28160 IsolatorMigrationDemo.pdb
-a--- 2014/10/10 9:30 23168 IsolatorMigrationDemo.vshost.exe
-a--- 2014/10/10 9:30 189 IsolatorMigrationDemo.vshost.exe.config
-a--- 2013/06/18 21:28 490 IsolatorMigrationDemo.vshost.exe.manifest
PS> $asmInfo = [System.Reflection.Assembly]::LoadFrom((dir .\IsolatorMigrationDemo.exe).FullName)
PS> $asmInfo.GetTypes()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False Form1 System.Windows.Forms.Form
False False Program System.Object
True False SomeClass System.Object
True False UserOfSomeClass System.Object
False False Resources System.Object
False False Settings System.Configuration.ApplicationSettingsBase
PS> $asmInfo.GetTypes() | ? { $_.Name -match 'Class' }
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False SomeClass System.Object
True False UserOfSomeClass System.Object
PS> $asmInfo.GetTypes() | ? { $_.Name -match 'Class' } | pfind
Method
------
Void MyMethod()
Void .ctor()
Void DoSomething()
Void .ctor()
PS> $asmInfo.GetTypes() | ? { $_.Name -match 'Class' } | pfind | pget | clip # この結果については、IsolatorMigrationDemo.v4.0.30319.v1.0.0.0.prig に貼り付けてください。
PS> $asmInfo = [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
PS> $asmInfo.GetTypes() | ? { $_.Name -eq 'messagebox' }
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False MessageBox System.Object
PS> $asmInfo.GetTypes() | ? { $_.Name -eq 'messagebox' } | pfind -m 'show\(system\.string\)'
Method
------
System.Windows.Forms.DialogResult Show(System.String)
PS> $asmInfo.GetTypes() | ? { $_.Name -eq 'messagebox' } | pfind -m 'show\(system\.string\)' | pget | clip # この結果については、System.Windows.Forms.v4.0.30319.v4.0.0.0.prig に貼り付けてください。
PS> exit
PS>
view raw 08_02.ps1 hosted with ❤ by GitHub

Visual Studio に戻り、IsolatorMigrationDemo.v4.0.30319.v1.0.0.0.prig と System.Windows.Forms.v4.0.30319.v4.0.0.0.prig に各々の間接設定を貼り付けます。ビルドが成功したら、サンプルを移行していきますよ!





Example Test 1 - Simple test using MessageBox
Isolator は、プロファイリング API による強力なメソッドの入れ替え機能に加え、JustMock と同様、Mock Object を生成する機能を持っています。Prig はそのような機能をサポートしていませんが、最初に説明した通りMoq と連携することで、それを実現することができましたね。
[Test]
public void MessageBoxShow_should_be_callable_indirectly()
{
using (new IndirectionsContext())
{
// Arrange
var mockMessageBox = new Mock<IndirectionFunc<string, DialogResult>>();
mockMessageBox.Setup(_ => _(string.Empty)).Returns(DialogResult.OK);
PMessageBox.ShowString().Body = mockMessageBox.Object;
// Act
MessageBox.Show("This is a message");
// Assert
mockMessageBox.Verify(_ => _("This is a message"));
}
}
view raw 08_03.cs hosted with ❤ by GitHub

Isolate.WhenCalled は、Prig の間接スタブ(この場合、PMessageBox.ShowString().Body)に、Moq.Mock.Setup でセットアップした Mock Object を割り当てることで、置き換えることができます。Isolate.Verify.WasCalledWithExactArguments は、Moq.Mock.Verify と機能的に同じですね。問題は無いでしょう。次へ行きますよ!





Example Test 2 - Complex Test
「複雑な」と付いていますが、そう難しいものではありません ( ̄ー ̄)
[Test]
public void UserOfSomeClassDoSomething_should_show_MessageBox_if_an_exception_is_thrown()
{
using (new IndirectionsContext())
{
// Arrange
PSomeClass.MyMethod().Body = () => { throw new Exception("foo"); };
var mockMessageBox = new Mock<IndirectionFunc<string, DialogResult>>();
mockMessageBox.Setup(_ => _(string.Empty)).Returns(DialogResult.OK);
PMessageBox.ShowString().Body = mockMessageBox.Object;
// Act
var user = new UserOfSomeClass();
user.DoSomething();
// Assert
mockMessageBox.Verify(_ => _("Exception caught: foo"));
}
}
view raw 08_04.cs hosted with ❤ by GitHub

特別な条件が無いのであれば、Isolate.WhenCalled(..).WillThrow は、Prig の間接スタブ(この場合、PSomeClass.MyMethod().Body)に、直接例外をスローする関数を割り当てることで、置き換えることができます。Isolate.WhenCalled(..).WillReturn や Isolate.Verify.WasCalledWithExactArguments は、前に説明しましたので・・・おっと、これで全部です!

ちなみに、対象が、MessageBox 処理があるにも関わらずテストコードを書きたくなるほど複雑な場合、設計をしくじっている可能性が高いと、個人的には思います。既存のコードやレガシーコードに対しては仕方がないでしょうが、こんなライブラリのような闇の力を、新規のプロダクトコードには使わなくて済むことを願いたいものですね (^^ゞ



0 件のコメント:

コメントを投稿