ログインしてさらにmixiを楽しもう

コメントを投稿して情報交換!
更新通知を受け取って、最新情報をゲット!

Green Bar Lover .NETコミュのお知恵拝借

  • mixiチェック
  • このエントリーをはてなブックマークに追加
テストしていて困ったのは、
抽象クラスのテストの書き方です。
皆様は書かれていますか?
サブクラスなら書き方分かるのですが・・

すいません、大雑把かつ初心者の質問で。

コメント(19)

(1) 既に派生クラスがあるなら、それを使ってテスト。
(2) なければ、テストしない。

という風にしてるかも (にやり)。

但し、或るクラスに対するテストというものが、そのクラスの「在り方 (仕様)」を記述するものだとすると、或る派生クラスに関するテストでは、抽象クラスのテストとして必要十分ではないかも。
抽象クラスはあくまでも抽象的概念を表すものなので、その具体的な在り方は、コンテキスト依存のような気がするし…


という訳で (謎)、テスト クラスの内部クラスとして、その抽象クラスの派生クラスを (コンテキストの数だけ) 作って、そいつをテスト、でしょうか。
# Java なら無名クラスが使えるのかも。
> (1) 既に派生クラスがあるなら、それを使ってテスト。
> (2) なければ、テストしない。

私も同じですね。

最初から抽象クラスを導入することは稀で、リファクタリングの結果として抽象クラスが抽出されることが多いです。この場合は既に幾つかの派生クラスがあってそれぞれにテストが存在しているはずなので(1)のケースですね。

抽象クラスのコンテキスト依存での在り方をテストする場合は、抽象クラスの派生クラスのダミーをテストクラスの内部クラスとして準備する方法と、モックを使う方法の2種類があるかも。
> 抽象クラスの派生クラスのダミーをテストクラスの内部クラスとして準備する方法

あ、これってモックか(笑)
.NETならNMockを使用する方法があるということで♪
> 抽象クラスのテストの書き方です。

抽象クラスのテストクラスを抽象クラスとしたことがあります.
テストクラスにテスト対象のインスタンスを生成する FactoryMethod を作りました.
まあ,賛否両論ある方法だと思いますけど (^^;

参考:tsuneさんの日記
http://mixi.jp/view_diary.pl?id=7830029&owner_id=95288
動的モックで抽象クラスをどう動かしたいかを設計して、静的モックでコンテキスト依存のテストを行うとこんな感じかな。

--
using System;
using System.Text;
using NUnit.Framework;
using NMock;
using NMock.Constraints;
using Sample.Core;

namespace Sample.Tests
{
 [TestFixture] public class AbstractMessengerTest
 {
  private AbstractMessenger target;

  [Test] public void HowItWorks()
  {
   IMock mock = new DynamicMock(typeof(AbstractMessenger));
   mock.Expect("SetHeader", new IsTypeOf(typeof(StringBuilder)));
   mock.Expect("SetBody", new IsTypeOf(typeof(StringBuilder)));
   mock.Expect("SetFooter", new IsTypeOf(typeof(StringBuilder)));

   target = (AbstractMessenger) mock.MockInstance;
   target.Message();

   mock.Verify();
  }

  [Test] public void MockSaidHello()
  {
   target = new HelloMessengerMock("tsune");

   Assert.AreEqual("こんにちは、tsuneです。", target.Message());
  }

  private class HelloMessengerMock : AbstractMessenger
  {
   private string name;

   public HelloMessengerMock(string name)
   {
    this.name = name;
   }

   public override void SetHeader(StringBuilder builder)
   {
    builder.Append("こんにちは、");
   }
   
   public override void SetBody(StringBuilder builder)
   {
    builder.Append(name);
   }

   public override void SetFooter(StringBuilder builder)
   {
    builder.Append("です。");
   }
  }
 }
}

using System;
using System.Text;

namespace Sample.Core
{
 public abstract class AbstractMessenger
 {
  public string Message()
  {
   StringBuilder builder = new StringBuilder();
   SetHeader(builder);
   SetBody(builder);
   SetFooter(builder);
   return builder.ToString();
  }

  public abstract void SetHeader(StringBuilder builder);
  public abstract void SetBody(StringBuilder builder);
  public abstract void SetFooter(StringBuilder builder);
 }
}
ウーン。どれもナルホド。という感じです。
(1)、(2)は隣の女子とそういう結論になってましたね。
やっぱり。

てらださん案の、テストクラスにテスト対象のインスタンスを生成する FactoryMethod を作りました
は斬新な感じです。

なんか、どうしてもテストコードないと不安な感じがして、
やらない!って選択肢にドキドキしちゃうので(笑
1度やってみようかと思います。

とりあえず明日、2人でまた相談して、書き込みします。

皆様ありがとございます!
初めまして。くまさんの隣の者です。
サンプルコードまで頂きありがとうございます。
なるほどという感じです。

ただ、そこで少し質問があるのですが、
サンプルコードの抽象クラスにはインターフェースが
ないのですが、これは通常ないものなのでしょうか。
NMockでテストする際には、実コードの方に
インターフェースがあることで、Mockでテストした場合に
実コードの動作が保証されると考えていたのですが、
実際にはどうなんでしょうか。

すみませんが、教えてください。
よろしくお願いします。
抽象クラスとインターフェースに関するお話になると思います。長文になると思いますが、ご了承下さいね。

まず、NMockは抽象に対して仮実装を行うものと考えて頂ければいいのかな。

この場合の抽象には抽象クラスとインターフェースがあります。抽象クラスもインターフェースも同じようにDynamicMockのコンストラクタに引き渡すことができます。

サンプルの抽象クラスの場合は、SetHeader/SetBody/SetFooter という実装のない抽象メソッドがありますね。この部分がひとみさんが今までNMockを使う場合に使用していたインターフェースのようなものと考えて下さい。

サンプルでNMockを使用したHowItWorksテストでは、Messageメソッドが呼び出されたら、StringBuilderをパラメータにしてSetHeader、SetBody、SetFooterの各メソッドが順番に呼び出されていることをテストしています。つまり、戻り値のテストをしているのではなく、抽象クラスがどのように振舞うかをテストしているわけです。

では、AbstractMessengerクラスがインターフェースであった場合を考えてみます。

public interface IMessenger
{
 string Message();
 void SetHeader(StringBuilder builder);
 void SetBody(StringBuilder builder);
 void SetFooter(StringBuilder builder);
}

この場合、次のようにIMessengerインターフェースの実装クラス毎にMessageメソッドを記述しないといけません。これは重複ですね。またこれではMessageメソッドを呼び出した場合にSetHeader、SetBody、SetFooterの各メソッドが順番に呼び出される保証はありません。

public class HelloMessengerMock : IMessenger
{
 private string name;

 public HelloMessengerMock(string name)
 {
  this.name = name;
 }

 // 各派生クラス毎に同じ処理を書かなくてはいけない。
 public string Message()
 {
  StringBuilder builder = new StringBuilder();
  SetHeader(builder);
  SetBody(builder);
  SetFooter(builder);
  return builder.ToString();
 }

 public void SetHeader(StringBuilder builder)
 {
  builder.Append("こんにちは、");
 }

 public void SetBody(StringBuilder builder)
 {
  builder.Append(name);
 }

 public void SetFooter(StringBuilder builder)
 {
  builder.Append("です。");
 }
}

このように、抽象クラスは各派生クラスに共通のデフォルトの実装(この場合はMessageメソッド)があるような場合に使うといいでしょう。サンプルコードではMessageメソッドの中でSetHeader/SetBody/SetFooterの各抽象メソッドが順番に呼び出されていますよね。このようにメソッドの呼び出し順を抽象クラスで定義して、SetHeader/SetBody/SetFooterの実装は派生クラス側で個々に拡張できるようにしておけば、AbstractMessengerクラスのどの派生クラスでもSetHeader/SetBody/SetFooterの各メソッドが順番に呼び出されることが保証されますよね。このようなメソッドをTemplate Methodと呼びます。

サンプルコードはこんな感じで抽象クラスがどのように振る舞うかをNMockでテストして、コンテキスト依存部分はサブクラスのダミーをつくってそれでテストをすればいいんじゃないかなっていう提案です♪
我々、色々混乱していて、質問とか適切じゃなかったです。
すいません。
が、ハッとしました。
抽象クラスってステキなものなんですね。

振る舞いをテストして、コンテキストをテストするという二段構え、とっても納得しました。
2人でため息です(笑

で、立て続けに再質問です。

-------
>まず、NMockは抽象に対して仮実装を行うものと考えて頂ければいいのかな。

ということは、下記のXXXXの部分には、抽象クラスかインターフェースしか入れられないっていうことなんですよね?
IMock mock = new DynamicMock(typeof(XXXX));

※ちなみに先ほどはペアライトしました。
今回は別々で行きます!
ありがとうございます!!
くまさんも書かれていますが、
二人して感動しています。素敵です。

>まず、NMockは抽象に対して仮実装を行うものと考えて頂ければい>いのかな。
>
>この場合の抽象には抽象クラスとインターフェースがあります。
>抽象クラスもインターフェースも同じようにDynamicMockのコン
>ストラクタに引き渡すことができます。
ここにとても納得しました。
抽象クラスもDynamicMockのコンストラクタに
渡すことができるのでインターフェースが不要なのですね。

それで、サンプルコードに感動しながら、
くまさんの質問した内容から、自分たちの
別の問題へ摩り替えてました。
ほんと、手探り状態で混乱していました。
すみません。。
少しでもお二方のお役に立てたのなら何よりです♪

> ということは、下記のXXXXの部分には、抽象クラスかインターフェースしか入れられないっていうことなんですよね?
> IMock mock = new DynamicMock(typeof(XXXX));

もしかしたら普通のクラスでもvirtualなメソッドとかあればNMockでオーバライドできたりするのかな。これは試してないのでわかんないです。試してみてね(笑)

また、抽象クラスとインターフェイスの使い分けの指針に関してはFujiwoさんの素晴らしい指針を参考にされるといいと思います。
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?mode=viewtopic&topic=18130&forum=7&start=8

#てらださんの抽象テストクラスでFactoryはまだやったことがないのですが、Builderパターンの場合とかに有効そう。
NMockってすごいですね!不覚にも感動してしまいました.
ドットネッターが羨ましい.
C++じゃそんなことできないもんなぁ...
ちょっと出遅れました。

テストクラスのソースの中に、テストに関するもろもろを集約することを心がけると良いと思います。
テストの依存度を下げることや、見易さの観点からも有効だと思います。

内部クラスも、NMockも、この意味でも最適ですね。

最近、.Configに依存するテストなども、文字列で定義を書いてメモリのストリームを作成するなどのテクを使って、テストしています。
今度Blogに、詳細を書きます。
> 今度Blogに、詳細を書きます。

是非、是非。楽しみにお待ちしております。
追加で、更に、横道

インターフェイスをテストするなんてことを考えてみる。

目的
 1.特定の単一の目的にフォーカスされているか
 2.利用手順が明確であるか
 3.メソッドの粒度が揃っているか

これらを確認(ある意味設計)するためや、利用方法を明示することを目的にテストを書くことは有効だと思います。

結局、抽象クラスのテストでも、目的を考えると答えが出てくると思います。

くどいですが、抽象クラスのテストに、既存の派生クラスを利用してテストを書いてしまうと、派生クラスにもテストが依存してしまうので、お勧めとしては、NMockなどを利用して、テストクラスあるいはテストコードファイルに集約するほうが良いと思います。
結局、ケースバイケースですけどね。
インターフェイスのテストの目的、素晴らしいですね。どれもインターフェイスの設計には欠かせないものですね。インターフェイスのテストでもNMockでテストを書いて、そのインターフェイスを実装する目的や利用方法を明確にしておくことはとても意味があることだと感じました。コメントでそれらを記述するよりはテストの方が意図が明確になりますしね。早速実践してみよう。

> 抽象クラスのテストに、既存の派生クラスを利用してテストを書いてしまうと、派生クラスにもテストが依存してしまうので

なるほど。抽象クラスのテストは派生クラスに依存しないようにすべきですね。これ重要!
目的、すごく大事ですね。
今、既存のF/Wに対してテストコードをつけるという作業なのですが、
ついつい、本来の目的とかを忘れてしまいます。
それから、依存度を下げるもすごく大事だなと実感してます。

いっぱい考えることがありすぎで、ショートしそうです(笑
てらださんへ

mockpp
http://mockpp.sourceforge.net/index-en.html

こんなのありました。

ログインすると、みんなのコメントがもっと見れるよ

mixiユーザー
ログインしてコメントしよう!

Green Bar Lover .NET 更新情報

Green Bar Lover .NETのメンバーはこんなコミュニティにも参加しています

星印の数は、共通して参加しているメンバーが多いほど増えます。

人気コミュニティランキング