7

次のテンプレートメソッドの実装を想定します。

public abstract class Abs
  {
    void DoYourThing()
    {
      log.Info("Starting");
      try
      {
        DoYourThingInternal();
        log.Info("All done");
      }
      catch (MyException ex)
      {
        log.Error("Something went wrong here!");
        throw;
      }
    }

    protected abstract void DoYourThingInternal();

  }

Absさて、クラスをテストする方法についてたくさんの情報があり、それDoYourThingInternalが呼び出されていることを確認してください。ただし、クラス
をテストしたいとします。Conc

 public class Conc : Abs
  {
    protected override void DoYourThingInternal()
    {
      // Lots of awesome stuff here!
    }
  }

conc.DoYourThing()これは、すでに個別にテストされている親メソッドを呼び出すため、 実行したくありません。

オーバーライドメソッドのみをテストしたいと思います。

何か案は?

4

5 に答える 5

5

あなたは質問に「tdd」というラベルを付けましたが、この「問題」に遭遇したときにその原則に従っているとは思えません。

本当にtddに従った場合、ワークフローは次のようになります。

  1. まだ実装されていないロジックのテストを作成する
  2. このテストをグリーンにするための最も簡単な実装を実装します(たとえば、Conc1のロジック)
  3. リファクタリング
  4. まだ実装されていない他のロジックのテストを作成します
  5. このテストをグリーンにするための最も簡単な実装を実装します(たとえば、Conc2のロジック)
  6. リファクタリング

「6」では、Conc1とConc2がいくつかのロジックを共有しているため、テンプレートメソッドを実装することをお勧めします。それを実行し、テストを実行して、ロジックが引き続き機能することを確認します。

ロジックを検証するためのテストを作成します。実装がどのように見えるかをベースにしないでください(=テストから開始します)。この場合、ロジックが機能することを確認するテストの作成を開始します(ロジックは後で具象型に配置されます)。はい、それはいくつかのコード行(抽象クラ​​スの1つ)が複数回テストされることを意味します。しかし、それで何?テストを作成するポイントの1つは、コードをリファクタリングできる必要がありますが、テストを実行することでコードが機能することを確認できることです。後でテンプレートメソッドパターンを使用したくない場合は、理想的な世界では、テストを変更する必要はなく、実装を変更するだけです。

テストするコード行を考え始めると、IMOはテストを作成することの利点の多くを失います。ロジックが機能することを確認する必要があります。代わりに、このテストを作成してください。

于 2012-09-25T17:38:24.413 に答える
3

「問題」の一部は、クラスの外部から保護されたメソッドを呼び出す方法がないことだと思います。Conc新しいパブリックメソッドから派生して提供するモッククラスはどうですか?

public class MockConc: Conc
  {
    void MockYourThingInternal()
    {
      DoYourThingInternal()
    }
  }
于 2012-09-24T17:45:30.200 に答える
2

DoYourThingInternal()とにかく抽象クラスを単独でインスタンス化することはできず、2つのメソッドは常に一緒に実行されるため、(分離しDoYourThing()てテストできる2つの別々のコードモジュールのように)分離しているとは考えません。さらに、DoYourThingInternal()クラスのすべての保護されたメンバーにアクセスでき、それらを変更して、に潜在的な副作用をもたらす可能性がありますDoYourThing()DoYourThing()したがって、の具体的な実装から完全に分離してテストすることは危険だと思いますDoYourThingInternal()

DoYourThing()ただし、これは、Absのすべての実装で同じである必要がある、の予想される動作との予想される動作を個別にテストできないことを意味するものではありませんDoYourThingInternal()

から期待される一般的な動作のテストを定義する(抽象)基本テストクラスを使用できますDoYourThing()。次に、Absの実装と同じ数のテストサブクラスを作成し、各実装の詳細について単体テストを行います。

基本テストクラスからのテストが継承され、サブクラスのテストを実行すると、の継承されたテストDoYourThing()も実行されます。

public abstract class AbsBaseTest
{
  public abstract Abs GetAbs();

  [Test]
  public void TestSharedBehavior() 
  {
    getAbs().DoYourThing();

    // Test shared behavior here...
  }
}

[TestFixture]
public class AbsImplTest : AbsBaseTest
{
  public override Abs GetAbs()
  {
    return new AbsImpl();
  }

  [Test]
  public void TestParticularBehavior()
  {
    getAbs().DoYourThing();

    // Test specific behavior here
  }
}

http://hotgazpacho.org/2010/09/testing-pattern-factory-method-on-base-test-class/を参照してください

ただし、抽象テストクラスの継承がすべての単体テストフレームワークでサポートされているかどうかはわかりません(NUnitはサポートしていると思います)。

于 2012-09-26T15:38:48.690 に答える
0

Absにインターフェースを貼り付けて、それをあざけるのはどうですか?呼び出しを無視しますか、それともそれらに期待を設定しますか?

于 2012-09-24T16:48:40.637 に答える
0

あなたはそれをいくつかの方法で行うことができますが、その多くはすでにここに文書化されています。私が通常採用するアプローチは次のとおりです。テストケースに具象クラスから継承させます。

public ConcTest : Conc
{
    [Test]
    public void DoesItsThingRight()
    {
         var Result = DoItsThingInternal();
         // Assert the response
    }
}

お役に立てば幸いです。

ブランドン

于 2012-09-27T03:22:11.907 に答える