良いテストの書くにはどうすればよいのだろうと思っていたところに、JUnit実践入門が発売されました。
JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)
- 作者: 渡辺修司
- 出版社/メーカー: 技術評論社
- 発売日: 2012/11/21
- メディア: 単行本(ソフトカバー)
- 購入: 14人 クリック: 273回
- この商品を含むブログ (68件) を見る
これは良いと早速購入し、C# 4.0 + NUnitで試してみることにしました*1。
テストランナーについて
VisualStudio2012からはMSTestもExpress版で使えるとのことですが、JUnitと似ていた方が理解しやすいだろうと、NUnitを選びました。
ちなみに、MSTestの使い方については、以下にまとめられています。
JUnit実践入門 MSTest用パッチ #TddAdventJp - 亀岡的プログラマ日記
参考資料について
以下にお世話になりました。ありがとうございました。
- NUnitの全貌 ~ 基本から、最新バージョンの新機能まで (1/8):CodeZine
- 2009-05-21 - 廻る技術の覗き穴
- NUnit 2.5 が便利すぎる - ぐるぐる~
- Java やってる人が C# を使うとはまること - ぐるぐる~
- TestCase 属性などによるテストコードのリファクタリング - ぐるぐる~
- VisualC#2010ExpressにおけるNUnitの基本的な設定の仕方 - 森理 麟(moririring)のプログラマブログ
- VisualStudio2010 Express + NUnit (最新版2.5.8Released)を使ってテストコードを書いてみる - Road to NAgiler
モック・スタブのフレームワークについて
以下の記事を読み、記載されていたRhino Mocksを使うことにしました。
#123 Rhino.Mocksを使ってみた « C# « a wandering wolf
クイックリファレンスは、以下にありました。
while(coding){DoTDD();}: Rhino Mocks Arrange / Act / Assert (AAA) Syntax Quick Reference
ただ、stackoverflowではRhino Mocksが「Keep in mind that Rhino is no longer developed」とも書かれていましたし、公式ページの動きは2011年くらいで止まっているようなのが気がかりでした。
c# - Rhino Mocks - Do we really need stubs? - Stack Overflow
その後、「The Art of Unit Testing, Second Edition」という書籍を読んだところ、Rhino Mocksの代わりに NSubstitute がおすすめされていたため、そちらも合わせて使うことにしました*2。
また、NUnitプロジェクトでも使われているようです。
NuGet Gallery | NUnit.Mocks 2.6.4
なお、NSubstituteのPartial Mocksについては、今のところ実装されていないようです。
Support for Partial Mocks · Issue #41 · nsubstitute/NSubstitute · GitHub
Moqについては、以下の記事に使い方がまとめられていました。
Dummy vs. Stub vs. Spy vs. Fake vs. Mock | Niraj Bhatt - Architect's Blog
環境
- Windows7 x64
- Visual Studio 2010 C#
- 対象のフレームワーク: .NET Framework 4
- プラットフォームターゲット: AnyCPU
- NUnit 2.6.2 (NUnit-2.6.2.msi)
- Rhino Mocks 3.6
- NSubstitute 1.4.3.0
ちなみに、NUnitのエラーを避けるためにAnyCPUを、Rhino Mocksなどを使うために非ClientProfileの.NET4を、それぞれ選択しています。
参考:64ビットのWindowsOS上でNUnitがテストプロジェクトをロードできない - sappokori's diary
注意
残念ながら以下の一部の課題は、テストの書き方が分からなかったために解くことができませんでした。
今後フレームワークを理解する中で書き方が分かれば、追記していく予定です。
- ch20.ex03
- ch20.ex05
- ch20.ex06
演習問題を解いた時に考えたこと
ch18.ex02
「DivideByZeroException」というそれらしい例外があったため、そちらを使いました。
ch18.ex05
Dictionaryを使っている上、int型のデフォルト値が0なので、SingleOrDefault()を使いました。
public int GetNum(Item item) { return values.Where(v => v.Key == item.Name).SingleOrDefault().Value; }
ch18.ex06
C# 4.0が使えるので、System.Threading.Tasksの書き方をメインにしました。
また、C# 4.0のおかげで、JavaのCountDownLatchと似た、System.Threading.CountdownEventを使ったものも書いてみました。
それ以前のバージョンのC#の場合も、以下を参考にすれば書けそうな気がしますが、今回は書きませんでした。
multithreading - Is there a C# equivalent to Java's CountDownLatch? - Stack Overflow
なお、System.Threading.Threadの場合のCountdownEventの書き方は以下を参考にしています*3。
Threading in C# - Part 2 - Basic Synchronization
ch19.ex01
JUnitの「カスタムMatcher」は、NUnitでは「カスタムConstraint」と呼ぶようです。
書き方については以下を参考にして、NUnit.Framework.Constraints.Constraintを継承して作成しました。
NUnit のカスタムConstraint
なお、オーバーライドなどの内容は以下のとおりです。
NUnit GUI上でやりたいこと | 方法 |
独自の検証 | Matches()をオーバーライド |
「Expected:」の内容を上書き | WriteDescriptionTo()をオーバーライド |
「But was:」の内容を上書き | WriteActualValueTo()をオーバーライド |
「Expected:」の一行上に文字列を追加 | Assert.That()の第3引数として、追加したい文字列を渡す |
ch19.ex06
enumの扱いがJavaとは異なるため、enumの各要素の値を文字列化するのはToString()で行いました。
動作スピードは遅いようですが、今回は手軽さを優先し、こちらを採用しました。
2008-01-24 - 当面C#と.NETな記録
また、NUnitのAssumeについては以下の公式ドキュメントを参考にしました。
NUnit - Theory
ch20.ex01
Datetimeオブジェクトをコンストラクタで渡す方法と、処理を別クラスで移譲した方法の2種類を書きました。
なお、C#にはパッケージプライベートという概念がないため、「sut.cal = mockcal」のような形では値を渡せませんでした。
残念ながらより良い方法が浮かばなかったため、以下を参考に、後者の方法でも別クラスをコンストラクタで渡しています。
testing - How write stub method with NUnit in C# - Stack Overflow
また、前者についてはリフレクションを使って、強引にprivateフィールドを書き換えています。
参考:C# で private メソッドを呼んでみる - 割と普通なブログ
ch20.ex02
Rhino Mocks・NSubstituteの2種類を使って書いています。
なお、試しに正しくない例外をexpectedしてみたところ、NSubstitute + NUnit GUI上でエラーメッセージが欠けました。
ToolTipsやCopyしてテキストファイルにペーストした場合は欠けませんでした (NSubstituteの方がエラーメッセージは長かった)。
そのため、もしかしたら、NUnitの制限なのかもしれません。
(左:Rhino Mocks、右:NSubstitute)
ch20.ex03
テストでstreamまわりをどう実装すれば良いのか分からず、テストコードを作成できませんでした。
ch20.ex04
こちらも、Rhino Mocks・NSubstituteの2種類を使って書いています。
ch20.ex05
.NETではサーブレットをどう表現すれば良いか悩み、コードを書けませんでした。
JavaとC#の書き方比較で参考にしたところ
ありがとうございます。
- 可変長引数 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C
- 列挙体内(Enum)の定数の値の配列を取得 - 強火で進め
- Dictionaryクラスを簡単に初期化するには?[C# 3.0] − @IT
- 4-24. 例外処理 - 2 - 独自の例外オブジェクトを作る。 - C# 入門 & 実践
- final const readonly - ぷっぷら帳
- What is the equivalent of Java's final in C#? - Stack Overflow
- C# - 指定した年と月に含まれる日数を取得する
- .NET開発における非同期処理の基礎と歴史 − @IT