.NET な開発者のための Test-Driven Development に関する話題なら何でも。
■ TDD に関係無さげな記事 (スレッド) を起こすときはタイトルの先頭に「【雑談】」等と付けて下さい ■
こんにちは。
テスタブルな設計ということでリポジトリパターンの話です。
今、ASP.NET MVCでちゃんと単体テストを書こうと思ってます。
するとDBアクセス周りはリポジトリパターンで実装するのが定番ですよね。
で、ネット上のサンプルなどを見ながら、
リポジトリパターンで実装はしてみました。
単純なCRUDを書く分には問題はありません(殆どサンプルのまま)。
しかし、モデルの中にモデル(下位モデル)を持つような場合に違和感を感じました。
サンプルのままの設計だと、モデルと下位モデルの関係のところはテストできません。
つまり、コントローラーのテストしかできてない状態です。
こういった場合、モデルと下位モデルの間にもリポジトリのようなものを置くのが良いのでしょうか。
http://twitter.com/biac/status/90758779393679360
先週のわんくま名古屋で出たTDD対決ネタ: なにがあったっけ? f(^^; ひとつは思い出した。⇒TDD三原則から外れて、使いもしないenumの0の定義を書いちゃうのは良いか悪いか? #tddnet
http://twitter.com/biac/status/90759217723613186
あ、それもあった #tddnet QT @guicheng: テストコードのコピペはアリや否や
>>1
自動実装プロパティをテストするか否か。
自動実装プロパティの場合、setしたものをgetできて当然なので、テストは不要という考え方がある。でも、プロパティの中身を実装する可能性があるのでテストしておくという考え方もある。
ちなみに私はテストする派。
>>1
「どっちにする?」というよりも「どうやってやる?」と聞きたいパターンですが。
シングルトンオブジェクトのテスト方法。
staticなオブジェクトだけあって、一度テストすると中身が変わっちゃいます。
んで、実装によっては現状復帰不可能。
こういった場合のテストって、どうやりましょう?
>>1
セッションアンケートを見ると、けっこう好評だったようです。
参加していただいた皆様、ありがとうございました。
で、やってるときに気付いたネタ。
次にやるときのために、メモっておきます。
・Enum の定義は先に全部書いちゃう?
テストケースによることなく、Enum 定義を全部書いてしまう。
vs
テストケースを書くごとに、Enum の要素をひとつずつ書く。
※ 後者はこんな感じ。
テストケースをひとつ書いて、GREEN にするために、
Enum 信号{ 赤, }
またテストケースをひとつ書いて、
Enum 信号{ 赤, 黄, }
次の RED → GREEN で、
Enum 信号{ 赤, 黄, 緑, }
◆ 作業場所の確保
→ Wiki は用意した。 http://www45.atwiki.jp/nunitjp/pages/1.html
チケット管理鯖とか… なくていいよね。(たぶん)
※ マニュアルの全タイトルを書きだしてみた。けっこうあるぞ
http://www45.atwiki.jp/nunitjp/pages/13.html
メンバーリスト: http://www45.atwiki.jp/nunitjp/pages/14.html (2011/
◆ メンバー間の連絡方法
オープンで進めるなら、 この掲示板と twitter で OK だと思うのだが、 これはみんなの意見を聞いてみないと。
クローズドでやるなら、 たとえば Facebook 内に秘密グループ作るとか。
ブログ記事 「[イベント] 予告 - TDD Boot Camp 名古屋 7/10-11 (仮)」
http://www.tdd-net.jp/2010/04/--tdd-boot-camp.html
に書いたように、 名古屋で TDD Boot Camp が計画されています。
細かいことはブログでは追いきれないので、 ここにメモっていきます。
もちろん、 TDDBC名古屋についての提案・質問などなどありましたら、 書き込んでやってください。 (きっと @bleis さんが答えてくれます f(^^; )
>>6
参加申し込みは、 今月末くらいから。
> akineko:今PCが使えないので確認出来ないんだけどTDDBC名古屋の開催決定してるってことなのかな? [http://twitter.com/akineko/statuses/13291274103]
> bleis:@akineko 申し込みはまだ開始してないっす。多分、5末までには開始するはず! [TDDBC名古屋、7/10、11に金山で開催] [http://twitter.com/bleis/statuses/13291591486]
◆◆◆ 公式ページ、オープン!! ◆◆◆
参加申し込みはもうしばらくお待ちください。
http://twitter.com/bleis/statuses/13784044025
@bleis:ブログ更新。TDD Boot Camp名古屋の公式ページ http://d.hatena.ne.jp/bleis-tift/20100511/1273578491
http://twitter.com/bleis/statuses/13784215490
@bleis:ちなみに値段は多分もうちょっと(本当にちょっと)下がる予定
http://twitter.com/kaorun55/statuses/13784235692
@kaorun55:@bleis 了解です。あと一日目の開始時間って、何時で考えてます?
http://twitter.com/bleis/statuses/13784277176
@bleis:@kaorun55 今のところ、11時予定で、早くても10時半くらいを考えてます
◆◆◆ 参加申し込み受付開始です!! ◆◆◆
http://d.hatena.ne.jp/bleis-tift/20100524
> 2010-05-24
> [etc]TDD Boot Camp 名古屋の申し込みを開始しました
/*
7月10日 TDD Boot Camp名古屋(愛知県)
40 人 max で集まれば、1,000 円程度キャッシュバックできそうです。
*/
申し込みサイト
http://kokucheese.com/event/index/2467/
★ 20:30現在で残り14名です。 お早めにどうぞ! ★
>>9
募集開始からものの数日で定員になっちゃいました。
溢れちゃった方、残念でしたが、きっとまたすぐにどこかで開催されますよ。
で。 bleis さんとこのブログで、 言語のアンケート結果などが出ました♪
http://d.hatena.ne.jp/bleis-tift/20100623/1277297853
> TDD Boot Camp で使う言語、環境について
(2010-06-23)
開催中の呟きマトメが↓こちらに。
http://togetter.com/li/34942 TDDBootCamp名古屋(1日目)
http://togetter.com/li/34943 TDDBootCamp名古屋(2日目)
http://togetter.com/li/35035 TDDBootCamp名古屋(後日談)
6/12 の「わんくま名古屋勉強会 #13」
http://bluewatersoft.cocolog-nifty.com/blog/2010/05/612-13-vs2010-1.html
に於いて、「TDD 道場」 第3回を開催します。
今回は、 ユニットテストしにくいものをネタにしてみます。
同じネタで、 違う解決パターンを 2つやってみようと思います。
本来の 「TDD 道場」 とはちょっと違って、 交代してもらう人が少なくなってしまいそうです。 ごめんなさい。
後半に、 NMock 得意な人を求む! f(^^;
あ。 環境は C# + NUnit の予定です。
当日のスライドとソースコードを公開しました。
http://bluewatersoft.cocolog-nifty.com/blog/wankuma-documents01.html#nagoya13
今回のお題は、システムクロックを参照する部分を切り出して、テスト用にスゲ換え出来るようにすること。
DateTime.Now で現在時刻を取得できますが、これ実はプログラムの外の世界とインターフェースしてるんですね。外とインターフェースする部分は、テスト時に別物に置き換えられるように設計すると、テストもしやすいし、変更にも強いプログラムになります。
事前に何も知らせず、その場でペアプロしていただいた @bleis さんと @a_hisame さん。 システムクロックを取得してくる部分のフェイクオブジェクトをきっちり作ってくれて、ユニットテストできるようにしてくれました。 じつにお見事でした。
当日の呟きを @nakaji さんが纏めてくれていました。
http://togetter.com/li/28808
TDD道場@わんくま名古屋#13 VS2010 CLT
コメント
>>6
詳しくありがとうございます。
確かにDIコンテナを導入するとなると、一気に壁が上がる感じはあります。
そうすると、各階層ごとにリポジトリを作る感じ(モックの場合でもテスト側からモックを渡す≒リポジトリパターンだと思うので)になってしまうという理解でいいでしょうか。
例えば、上に出した例で、Controller、Model(Publisher)、Model(Book)という場合、Controller → Interface → Publisher → Interface → Book → Interface → LINQ to SQLとなる感じでしょうか。
実際のところ、TDDで進めているところはこんな感じになっているのだとしたら、ちょっとまだこの段階に進めそうにないと思いました(うちの平均的な社内スキル的に)。
テスト時にDBを切り離すのをあきらめ、テスト専用のDBを作って、テスト前にTransactionを貼り、テスト後にRollbackするとか、そういった対応も一般的にはアリなのでしょうか。
すみません。間違いました。
Mockを使う場合は、ControllerにPublisherのMockを外部から渡すような作りにするとか、PublisherにBookのMockを渡すようなつくりにするというだけで、Interfaceの定義はなくても大丈夫ですね。
>>7
> そうすると、各階層ごとにリポジトリを作る感じ(モックの場合でもテスト側からモックを渡す≒リポジトリパターンだと思うので)になってしまうという理解でいいでしょうか。
リポジトリパターンは、データにアクセスする部分をビジネスロジックから分離するために使います。その考え方を以下に示します。
MVC の Model の中身を、まず大きく二つに分けます。
(両方合わせて MVC の Model)
+---------+ +-----------+
|ロジック |→|DBアクセス |
+---------+ +-----------+
DBアクセスの部分は、勝手に作るとロジックとのインターフェースがバラバラになるし、ロジックとガッチリくっついてしまいがちです(密結合)。
そこで、DBアクセスの部分に、ロジック向けの「玄関」を共通する形で作ってやります。
(両方合わせて上図のDBアクセス部分)
+----+ +--------------+
|玄関|→|DBアクセス本体|
+----+ +--------------+
この玄関のカタチには色々ありますが、そのひとつがリポジトリパターンです。
+----------+ +--------------+
|玄関 | |DBアクセス本体|
|リポジトリ|→|LINQ to SQL |
+----------+ +--------------+
そして、「玄関」のカタチをインターフェースとして定義してやれば、DBアクセス部分全体をすげかえる (ロジックからDBアクセス部分への依存性を切り替える) ことが可能になるわけです。
ここでは「玄関」がひとつだけであるように描きましたが、リポジトリパターンの場合は、原則として DB のテーブルごとにリポジトリ(「玄関」)を作ります。
例で言うと、Publisher や Book ごとにリポジトリを作るわけです。
・「玄関」の2つの入口(実装)
- PublisherRepository
- BookRepository
※念の為。この2つにビジネスロジックは入っていない。
・「玄関」の2つの入口(インターフェース)
- IPublisherRepository
- IBookRepository
※これに合わせたモックを作って、切り替える。
全体の構成は次のようになります。●印が上記のインターフェースです。
View → Controller → [Model: [ビジネスロジック] → [DBアクセス: ●[リポジトリ(玄関)] → [LINQ to SQL] ] ]
>>7
> テスト時にDBを切り離すのをあきらめ、テスト専用のDBを作って、テスト前にTransactionを貼り、テスト後にRollbackするとか、そういった対応も一般的にはアリなのでしょうか。
.NET では、アリです。
TransactionScope が使えるなら、割と簡単に実現できます。ただし、スローテスト(実行に時間が掛かるテスト)になってしまいますから、ビジネスロジックのテストを全部これでやるのはお勧めしません。
TransactionScope と LINQ to SQL の組み合わせ例
http://msdn.microsoft.com/ja-jp/library/bb387002.aspx
> 方法 : データ送信をトランザクションで囲む (LINQ to SQL)
テストコード側では using (TransactionScope … を書き、そのスコープ内で実装のメソッドを呼び出すようにします。
そして、テストコード側で using を抜ける前に ts.Complete() を呼び出さないようにします(ロールバックされる)。
>>11
ありがとうざいます。だいぶイメージがつかめました。
Professional ASP.NET MVC 2を参照してみると、
似たような議論がありました。
依存性を切り離すために、
Controllerの引数を渡すような作りが基本だけれど、
Controllerへ渡す引数が多くなりすぎると複雑になる。
そんな場合は、DIコンテナを使った方が良いという話が
最終的な結論のようでした。
(英語苦手なので、斜め読みしかできてませんが)
ひとまず、TransactionScopeとRepository、
Mockを使い分けてやってみようかと思います。