2014年12月8日月曜日

非公開メソッドの入れ替え - from "Prig: Open Source Alternative to Microsoft Fakes" Wiki -

ソフトウェアテストあどべんとかれんだー2014 8日目!&まとまってきたドキュメントを日本語の記事にもしておくよシリーズ第 9 段!(元ネタ:Prigwiki より、FEATURES: Non Public Method Replacement。同シリーズの他記事:1 2 3 4 5 6 7 8

はじめましての方ははじめまして!ソフトウェアテストあどべんとかれんだー2014 8日目を担当させていただきます、@urasandesu こと杉浦と申します。

前日は、@hayabusa333 さんの、Ruby - Gauntltによるセキュリティテスト #SWTestAdvent - Qiita でしたね。セキュリティテスト自動化フレームワーク、そんなものもあるのだと興味深く拝読させていただきました。2014 年は、Heartbleed や、Apache Struts の脆弱性ShellshockPOODLE と、脆弱性の話題に事欠かない年になってしまいましたが、今後セキュリティに関するテストはますます重要になっていくんでしょうね。

さて、ソフトウェアテストに関連することということで、私からは打って変わって実装寄りのお話を。以前から私が作成しています Prig という、.NET 自動ユニットテスト向け迂廻路生成ライブラリについて、紹介させていただこうと思います。迂廻路生成?と思われるかもしれませんが、簡単に言うと、通常は行えない static メソッドや private メソッドの上書きをできるようにするというものです(.NET だと Microsoft Fakes、Java だと JMockit とかが有名どころでしょうか)。

題材は「既存ライブラリの非公開メソッドが絡む自動ユニットテスト」。言語は C# です。よく言われる通り、非公開なメソッドそのものをテストすることはよくないこととされていますが、テストで非公開なメソッドに対して何かしたくなることは、しばしばあるんじゃないでしょうか?例えば、テスト対象のメソッド内で使われる private な setter を持つプロパティに意味のある値を与えておきたいWeb にアクセスしにいってしまう private メソッドをモックに入れ替えたい、などなど。まあ、今まさに開発中のコンポーネントであれば、いくらでも対処方法はあるのですが、すでに稼働しているシステムだったり、外部から買い入れたコンポーネントだったりすると、途端に難易度が跳ね上がるのが困りもの。

ところで、C# の特徴的な機能の 1 つに、今から 7 年ほど前に出た C# 3.0 で追加された、拡張メソッドという機能があります。皆さんは拡張メソッドは好きですか?乱用するべきではないですが、その機能が、本質的に、そのライブラリがそのレイヤーでサポートしてほしいものであれば、設計上自然な API を実現できることがあるかと、私は思います。ただし、そのライブラリが、そのような拡張に対してオープンであるかどうかは、場合によるでしょう。特に、そのシグネチャに、非公開な属性の 1 つである internal なクラスが現れるようなメソッドが関係する場合は要注意。今回は、Prig によって、どのようにこの問題を解決するかを解説したいと思います。


以下の記事、ライブラリを使用/参考にさせていただいています。この場を借りてお礼申し上げます m(_ _)m
How to mock ConfigurationManager.AppSettings with moq - Stack Overflow
TDD, Unit testing and Microsoft Fakes with Sitecore Solutions
Basic mocking techniques - Stack Overflow
Hybrid Framework - http://our.umbraco.org
Unit testing Umbraco 7 | just this guy
How to Write 3v1L, Untestable Code
Generic Methods Implementation in Microsoft Fakes - CodeProject
Paulo Morgado - Mastering Expression Trees With .NET Reflector
Expression Tree Visualizer for VS 2010 - Home
mocking - Using Microsoft Fakes Framework with VSTO Application-Level Add-in an XML based Ribbon - Stack Overflow





目次

非公開メソッドの入れ替え
既存ライブラリに、以下のような DTO 群があるとしましょう:

「DB への接続」という副作用と、「テーブル自体のデータ」という状態を 1 つのクラスで管理しており、嫌な臭いを感じます。ただ、このライブラリを作ったニンゲンが、これ以上のリファクタリングをするモチベーションを持つことはないかもしれません。なぜならば、このライブラリだけ見れば internal なクラスが緩衝材としてあり、InternalsVisibleToAttribute を使えば、制限なくそのクラスにアクセスができるため、テストをするのに特に問題を感じないでしょうから。

さて、このライブラリはテーブルスキーマの自動生成ツールも提供しており、特定の列を自動生成してくれます。そのような列は、以下のような規約で命名されるとのことです:
  • <table name> + _ID ・・・ プライマリキー
  • DELETED ・・・ 論理削除フラグ
  • CREATED ・・・ 作成日時
  • MODIFIED ・・・ 更新日時
なるほどなるほど。そうすると「自動生成された列だけを取得する」や「手動で生成された列だけを取得する」などのようなことがやりたくなりますね。残念ながら、既存のライブラリは、そのような機能を提供していないとのこと。なので、今回は自分で作成することにしました。こんなシチュエーションでは拡張メソッドがピッタリでしょう。テストを書いてみます:

テーブル USER には、列 USER_ID、PASSWORD、USER_NAME、DELETED、CREATED、MODIFIED があるとします。そのテーブルに対し、拡張メソッド GetAutoGeneratedColumns を実行すると、自動生成された列が取得できるという寸法です。このままではビルドすら通りませんので、とりあえず以下のような最低限の実装を用意しました:

ほい、実行っと。NotImplementedException がスローされるでしょうから、とりあえずなコードを追記し・・・て・・・あれ?

ヴァー!変なとこで引っかかっとる!! ('A`)

実は、スタックトレースにも出力されているように、ULColumns は、メソッド ValidateState を使うことによって、列が変更可能かどうかを検証しています。テストケースにおいては、GetAutoGeneratedColumns を検証したいのですが、その前、users.Columns.Add(expected[0]); で例外がスローされていたわけですね。これはいけません・・・。

このような状況で、Prig を使うことで、不必要な検証を一時的に外すことができます。Prig をインストールし、その Assembly のスタブ設定を追加します:

以下のコマンドを実行し、ULColumns の設定をクリップボードにコピーします:

そうしましたら、追加されたスタブ設定ファイル(例:UntestableLibrary.v4.0.30319.v1.0.0.0.prig)にそれを貼り付け、ソリューションをビルドします:

上の準備が終わったら、以下のようにテストを書き直すことができるようになります:

こんどはどうでしょう?

よし!今度は NotImplementedException がスローされるという、意図した結果になりました。後は、テストを通すコードを書き、全てのテストが通ったらリファクタリングをし、新たなテストを追加する、という黄金の回転を回すだけ。良い感じじゃないですか?





付録
ところで、かの人が元のライブラリをどのように設計していれば、もっと簡単にテストができていたと思いますか?そもそも、状態を副作用から分離したライブラリとして再設計すべきだとは思いますが、既存のライブラリで、インターフェイスを変更するような再設計は難しいでしょうね。せめてなにかできることがあるとすれば、非 public なインターフェイスを public にするような変更ぐらいでしょう。

なお、次の点には注意してください:単にインターフェイスを公開する、例えば、ULTableStatus の全てのフィールドと、ULColumns のコンストラクタ .ctor(ULTableStatus) を公開するようなことをしてしまえば、簡単にデータの不整合が起きるようになってしまい、ライブラリが安全ではなくなってしまいます。ライブラリの安全性が保たれる範囲のインターフェイスだけを公開するべきでしょう。このケースでは、以下のような変更が考えられます:

ULColumns のコンストラクタは公開しましたが、ULTableStatus を直接指定することはせず、検証のためのメソッドだけを持ったインターフェイス IValidation を代わりに指定するようにしました。Prig を使って入れ替えたかったメソッド ValidateState が持つ機能を、外出ししたことになります。対象のメソッドは状態にアクセスはしますが、それを書き換えることはしません。従って、その部分を公開するだけであれば、ライブラリのデータの整合性は保ち続けられることになります。それから、ULTable の ULColumns を生成するプロパティを virtual 化します。

これらの再設計により、Prig を使わなければキーとなるメソッドに到達することすら難しかったテストは、以下のようにできます。Moq のような通常のモックフレームワークで、簡単にテストができるようになるのです:





終わりに
ソフトウェアテストあどべんとかれんだー2014 8日目、C# と自作ライブラリを題材に、自動ユニットテストにおける非公開メソッドの入れ替えを解説してみました。つい先月、V1.0.0 をリリースしたばかりということもあり、まだまだ問題もあるかと思いますが、もし興味を持っていただき、使っていただければ嬉しいです!問題などあれば、是非 @urasandesu 宛てにお気軽に mention 下さいませ (((o(*゚▽゚*)o)))

さて、私のまとまってきたドキュメントを日本語の記事にもしておくよシリーズ(1 2 3 4 5 6 7 8)はこれで終わりですが、ソフトウェアテストあどべんとかれんだー2014 はまだまだ続きますのでお見逃しなく!

明日は、@PoohSunny さん。よろしくどうぞ!!

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: をテストプロジェクトに変更してください。その後、以下のコマンドを実行します:

次に、Isolator のサンプルで使用しているメソッドのための間接設定を取得しましょう。PowerShell(コンソール)を開き、情報を取得するために以下のコマンドを実行します:

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 と連携することで、それを実現することができましたね。

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





Example Test 2 - Complex Test
「複雑な」と付いていますが、そう難しいものではありません ( ̄ー ̄)

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

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



2014年12月4日木曜日

移行サンプル:Telerik JustMock によるモック化③ 静的メソッドのモック化 - from "Prig: Open Source Alternative to Microsoft Fakes" Wiki -

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

3 回に渡る JustMock の公式ドキュメントにあるサンプルを実装するシリーズ、Part 3 となります。最後は、「Static Mocking samples」を Prig に移行してみましょう。


以下の記事、ライブラリを使用/参考にさせていただいています。この場を借りてお礼申し上げます m(_ _)m
How often are fakes assemblies generated? - Stack Overflow
Brownfield Development: Taming Legacy Code with Better Unit Testing and Microsoft Fakes
Nested Types in Generic Classes - Haibo Luo's weblog - MSDN Blogs
c# - Behavedbase in fakes - Stack Overflow
CLR Profiler - Documentation
Resize image in the wiki of github usin markdown - Stack Overflow
Behind iPhone's Critical Security Bug, a Single Bad 'Goto' WIRED
microsoft fakes only stub static property of a static class - Stack Overflow
Home Page - Code Impact - .NET Community Event
PowerShell で SIGPIPE 連鎖 - NyaRuRuが地球にいたころ
Generics and Your Profiler - David Broman's CLR Profiling API Blog - Site Home - MSDN Blogs





目次

準備
このサンプルでは、シリーズということもあり、1 つのソリューションに複数のプロジェクトを追加するという構成を採っています(前回前々回の記事もご参照くださいませ)。これまでのサンプルと同様、Package Manager Console と PowerShell を使って説明を続けたいと思います。各使用コマンドは以前のサンプルで解説していますので、詳細はそちらもご覧ください。

さて、間接スタブ設定を作成しましょう。Package Manager Console を開き、Default project: をテストプロジェクトに変更します

次に、Assembly を解析します。PowerShell(コンソール)を開き、以下のコマンドで情報を取得します:

Visual Studio に戻り、各間接設定 StaticMockingMigration.v4.0.30319.v1.0.0.0.prig、System.Web.v4.0.30319.v4.0.0.0.prig に貼り付けます。ビルドは通りましたか?さあ、実際に移行してみましょう!





Static Constructor Mocking
静的コンストラクタの間接スタブは、文字通り StaticConstructor という名前になります:






General Static Method Mocking
一般的な静的メソッドのモック化です。んー、これは Moq と連携したほうがわかりやすいでしょうね:

ところで、個人的には ExpectedException より Assert.Throws のほうが好きだったり。ExpectedException を使うケースだと、意図しない場所で例外が発生しても(例えば、上記の例だと、Foo.Execute(10) が例外をスロー時)、テストは成功してしまいますからね。





Mocking Static Property Get
静的プロパティの getter を入れ替えます。何度か説明してきているように思いますが・・・一応紹介しておきましょう (^_^;)






Mocking Static Property Set
静的プロパティの setter を入れ替えます:

検証の方法について、JustMock のサンプルとは若干違いがあります。個人的には、MockBehavior.Strict を指定すれば、条件によってメソッドが呼び出されているかどうかを再度検証する必要はないとは思いますね。条件を満たさないメソッドの呼び出しがあれば、自動的に例外がスローされますので。





Mocking Internal Static Call
internal な静的メソッドを入れ替える例です。コメントに残すだけでなく、例外がスローされないことも検証すべきでしょう:






Mocking Static Class
静的クラスのメソッドを入れ替えるサンプルです。説明すべきことはあまりないですね (^-^;






Mocking Current HttpContext
現在の HTTP コンテキストを入れ替えるサンプルです。本来であれば、元の処理は HTTP リクエスト中にのみ有効なのですが、もはや制限はありません。






Mocking Extension Methods
拡張メソッドの入れ替えです。結局のところ、拡張メソッドは静的メソッドですので、前までのサンプルと同じように入れ替えることができます:




2014年12月1日月曜日

移行サンプル:Telerik JustMock によるモック化② シールされたクラスのモック化 - from "Prig: Open Source Alternative to Microsoft Fakes" Wiki -

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

3 回に渡る JustMock の公式ドキュメントにあるサンプルを実装するシリーズ、Part 2 となります。今回は、「Sealed Mocking samples」を Prig に移行してみましょう。


以下の記事、ライブラリを使用/参考にさせていただいています。この場を借りてお礼申し上げます m(_ _)m
Unity を使って AOP - present
How do microsoft fakes' shims actually work internally? - Stack Overflow
c# - Good and free unit-testing alternatives to Telerik's JustMock - Software Recommendations Stack Exchange
Unable to create Fakes for Google APIs - Stack Overflow
Covering basics of unit testing with Typemock - .NET Unit Testing Tips
The Difference Between Unit Tests and Integration Tests - Typemock Isolator
Typemock Isolator Quick Start -
Home - Run Jekyll on Windows
Setup Jekyll on Windows - Yi Zeng
OctopressをWindows7にインストールしてみたメモ by @pon_zu on @Qiita





目次

準備
このサンプルでは、シリーズということもあり、1 つのソリューションに複数のプロジェクトを追加するという構成を採っています。前回のサンプルと同様、Package Manager Console と PowerShell を使って説明を続けたいと思います。各使用コマンドは前回のサンプルで解説していますので、詳細はそちらもご覧ください。

まずは、間接スタブ設定を作成しましょう:

あー・・・「やっべ!Default project: にテストプロジェクト設定してねえ!変なとこに設定が追加された!!!!」って方。

お気の毒さまですが、Prig はそれを削除するコマンドをサポートしていません(PreBuildEvent 内の解析ェ・・・)。恐縮ですが、手動で *.csproj を元に戻す必要があります。以下のイメージのように、\.prig" にマッチするタグ Reference、None、PreBuildEvent の全てを削除してください:









Git のようなバージョン管理システム下で作業することを強くお勧めしておきます (^q^)

次は、PowerShell(コンソール)による対象メソッドの絞り込みです。慣れれば一気にできるでしょう:

今回は、Moles の例以降、表に出てこなかったコマンド pfind(Find-IndirectionTarget)を使っています。実はこのコマンド、前回までのサンプルで、標準リフレクション API によって掛けていたざっくりとしたフィルタを、既に含んでいます。なので、上記のように手順の簡易化ができるようになるんですね。そうしたら、Visual Studio に戻り、前回と同様 SealedMockingMigration.v4.0.30319.v1.0.0.0.prig に貼り付けます。ビルドは通りましたか?さあ、実際の移行をしてみましょう!





Assert Final Method Call on a Sealed Class
シールされたクラスの final メソッドを置き換えてみましょう:

特定のインスタンスだけの置き換えるのでない時は、PProxy<original class name> を使うより、P<original class name> を使うほうが簡単かも。





Create Mock for Sealed Class with Internal Constructor
ところで、入れ替える型のコンストラクタが非 public だとどうなるのか、心配になったりします?:

はい!何も問題ありませんね!





Create Mock for Sealed Class with Interface
これはインターフェイスを使った振る舞いの例です。まずは直呼び出し:

次に、インターフェイスを通じた呼び出し:

加えて、明示的に実装されたインターフェイスのケースです。これについては、JustMock はサンプルを準備していないようですね。なので、一応念のため説明させていただきます:

明示的に実装されたインターフェースを入れ替える時は、間接スタブが特殊な名前になることだけに気を付ければ大丈夫です。通常の名前は、単に <メソッドの間接スタブ名> ですが、この場合の名前は、<名前空間> + <インターフェイスの間接スタブ名> + <メソッドの間接スタブ名> となります。