2010-12-08 7 views
4

アプリケーションがあり、単体テストや機能テストを想像したくないと想像してください。あなたは抽象クラスを持っているかもしれません、それをAbstractTestClassと呼ぶことにしましょう。そこからすべてのユニットテストが拡張されます。 AbstractTestClassは、この(JUnitの4を使用)のようなものになりますテストと継承の問題

:ここ

class AbstractTestClass { 
    boolean setupDone = false; 

    @Before 
    public void before() { 
     if(!setupDone) { 
      // insert data in db 
      setupDone = true; 
     } 
    } 
} 

は私が苦労してるものです。それはそれはWebTestCaseを拡張することを除いて、ほとんど同じクラスだ

class AbstractWebTestClass extends WebTestCase { 
    boolean setupDone = false; 

    @Before 
    public void before() { 
     if(!setupDone) { 
      // here, make a call to AbstractTestClass.before() 
      // init the interfaces 
      setupDone = true; 
     } 
     // do some more thing 
    } 
} 

:私は、Webインタフェースをテストする別の抽象クラスを抱えています。この設計により、インターフェイスのテスト時よりも単体テスト時に同じデータを得ることができます。

通常、このような問題に対処するには、継承よりも構成を優先するか、戦略パターンを使用する必要があります。

残念ながら、私はこの特定のシナリオで継承を超えた構成に賛成する考えがあまりなく、戦略パターンをどのように使用できるか分かりません。おそらく設計上の欠陥があり、ソリューション。

私の目標を達成するために、このアーキテクチャをどのように設計できますか?

+0

webtestcaseがabstracttestcaseを拡張していますか?あなたは問題が何であるかを明確に述べることができますか、理解していないIm ... – hvgotcodes

+0

はい。これは望ましいことですが、あなたはJavaで複数の継承を行うことができないので、私はこれでこだわっています:) – ALOToverflow

+0

...あ、私は今参照してください。 – hvgotcodes

答えて

2

私は次のようにこれを実装します:

class Example { 

class LazyInitStrategy implements Runnable { 
    private final Runnable operation; 
    private boolean done = false; 

    LazyInitStrategy(Runnable operation) { 
     this.operation = operation; 
    } 

    @Override 
    public void run() { 
     if (!done) { 
      operation.run(); 
      done = true; 
     } 
    } 
} 

private final class AbstractInit implements Runnable { 
    public void run() { 
     // insert data in db 
    } 
} 

private final class AbstractWebInit implements Runnable { 
    public void run() { 
     // here, make a call to AbstractTestClass.before() init the interfaces 
    } 
} 

class AbstractTestClass { 

    final LazyInitStrategy setup = new LazyInitStrategy(new AbstractInit()); 

    @Before 
    public void before() { 
     setup.run(); 
     // do some more thing 
    } 
} 

class AbstractWebTestClass extends WebTestCase { 

    final LazyInitStrategy setupInfo = new LazyInitStrategy(new AbstractWebInit()); 

    @Before 
    public void before() { 
     setupInfo.run(); 
     // do some more thing 
    } 
} 

}

確かに、これは非常に単純な解決策ですが、それが必要セットアップが完了したかどうかを確認するために、if/elseロジックの重複を排除します。 Runnableの使用はオプションです。私はこれをデモ目的のためだけに行いました。読んでいる世界ではおそらく別のインターフェースを使用します。

+0

戦略パターンのように見えますか? – ALOToverflow

+1

サンプルを更新して、より戦略パターンに見えるようにしました:-)。通常、コードでは、純粋なパターンの実装がありません(パターンはガイドラインに過ぎません)。 –

+0

あなたのソリューションをかなり使いました。ありがとう。 – ALOToverflow

0

重要なことは、コードを複製することではありません。このような状況で、私はあなたがする必要があるとして、データを設定し、その上に静的メソッドの束を持っている

MyScenarioTestUtil

クラスを作成します。セットアップからユーティリティメソッドを呼び出すことになります。そうすれば、すべてのコードを1か所にまとめることができます。

その組成を使用してから、実際には意味論の違い...

0

一般的にデザインが間違っていると思います。ユニットテストでは継承を使用しないでください。テストは分離され、本当に明白でなければなりません。多くの場合、あなたの場合のように、いくつかの補足的なオブジェクトを用意する必要があります。これはメソッドをテストして仕事をするのに役立ちます。そのような場合は、そのようなオブジェクトのビルダーを定義し、テストケースの外側のどこかに配置する必要があります。例えば

public void testMethodThatNeedsSomePreparedObjects() { 
    Foo foo = new FooBuilder() 
    .withFile("some-text.txt") 
    .withNumber(123) 
    .build(); 
    // now we are testing class Bar, using object of class Foo 
    Bar bar = new Bar(foo); 
} 

はこのように、あなたはFooBuilderがどこかに定義する必要があり、このクラスは、あなたが今stategyパターンまたは継承を使用してやろうとしているすべての作業を行います。単体テストを扱う際にはどちらも問題になります。