8

次のレガシーコードがあります。

public class MyLegacyClass
{
    private static final String jndiName = "java:comp/env/jdbc/LegacyDataSource"

    public static SomeLegacyClass doSomeLegacyStuff(SomeOtherLegacyClass legacyObj)
    {
       // do stuff using jndiName
    }
}

このクラスは J2EE コンテナで動作します。

ここで、コンテナの外でクラスをテストしたいと思います。

最善の戦略は何ですか?リファクタリングは基本的に許可されています。

LegacyDataSource へのアクセスは許可されています (テストは「純粋な」単体テストである必要はありません)。

編集: 追加のランタイム フレームワークの導入は許可されていません。

4

3 に答える 3

7

@Robinの戦略パターンの提案をより具体的にするために:(元の質問の公開APIは変更されていないことに注意してください。)

public class MyLegacyClass {

  private static Strategy strategy = new JNDIStrategy();

  public static SomeLegacyClass doSomeLegacyStuff(SomeOtherLegacyClass legacyObj) {
    // legacy logic
    SomeLegacyClass result = strategy.doSomeStuff(legacyObj);
    // more legacy logic
    return result;
  }

  static void setStrategy(Strategy strategy){
    MyLegacyClass.strategy = strategy;
  }

}

interface Strategy{
  public SomeLegacyClass doSomeStuff(SomeOtherLegacyClass legacyObj);
}

class JNDIStrategy implements Strategy {
  private static final String jndiName = "java:comp/env/jdbc/LegacyDataSource";

  public SomeLegacyClass doSomeStuff(SomeOtherLegacyClass legacyObj) {
    // do stuff using jndiName
  }
}

...そして JUnit テスト。私は、このセットアップ/ティアダウン メンテナンスを行わなければならないのはあまり好きではありませんが、静的メソッド (またはシングルトン) に基づく API を使用することの残念な副作用です。このテストで私気に入っているのは、JNDI を使用しないことです。(a) 高速に実行され、(b) 単体テストは doSomeLegacyStuff() メソッドのビジネス ロジックのみをテストする必要があり、実際のデータ ソース。(ちなみに、これはテスト クラスが MyLegacyClass と同じパッケージにあることを前提としています。)

public class MyLegacyClassTest extends TestCase {

  private MockStrategy mockStrategy = new MockStrategy();

  protected void setUp() throws Exception {
    MyLegacyClass.setStrategy(mockStrategy);
  }

  protected void tearDown() throws Exception {
    // TODO, reset original strategy on MyLegacyClass...
  }

  public void testDoSomeLegacyStuff() {
    MyLegacyClass.doSomeLegacyStuff(..);
    assertTrue(..);
  }

  static class MockStrategy implements Strategy{

    public SomeLegacyClass doSomeStuff(SomeOtherLegacyClass legacyObj) {
      // mock behavior however you want, record state however
      // you'd like for test asserts.  Good frameworks like Mockito exist
      // to help create mocks
    }
  }
}
于 2008-10-30T15:36:50.697 に答える
2

依存性注入を使用するようにコードをリファクタリングします。次に、好みの DI フレームワーク (Spring、Guice など) を使用してリソースを注入します。これにより、実行時にリソース オブジェクトと戦略を簡単に切り替えることができます。

この場合、データソースを挿入できます。

編集:新しい制限に基づいて、戦略パターンを使用して実行時にデータソースを設定することで、同じことを達成できます。おそらく、プロパティ ファイルを使用して、データソースを作成および提供する戦略を区別することができます。これには新しいフレームワークは必要ありません。同じ基本機能を手作業でコーディングするだけです。この正確なアイデアを ServiceLocator で使用して、Java EE コンテナーの外部でテストするときにモック データソースを提供しました。

于 2008-10-30T13:19:49.320 に答える
1

ここでの最善の解決策は、そのJNDIをローカルにバインドすることだと思います

従来のコードでは、次のように jndiName を使用しています。

DataSource datasource = (DataSource)initialContext.lookup(DATASOURCE_CONTEXT);

したがって、ここでの解決策は、ローカル (またはテストデータ用に持っているもの) を次のような JNDI にバインドすることです。

  BasicDataSource dataSource = new BasicDataSource();
  dataSource.setDriverClassName(System.getProperty("driverClassName"));
  dataSource.setUser("username");
  dataSource.setPassword("password");
  dataSource.setServerName("localhost");
  dataSource.setPort(3306);
  dataSource.setDatabaseName("databasename");

そしてバインディング:

Context context = new InitialContext();
context.bind("java:comp/env/jdbc/LegacyDataSource",datasource); 

または似たようなもの、お役に立てば幸いです。

幸運を!

于 2008-10-30T14:05:45.607 に答える