すべて、
いくつかの古風なJavaコード(インターフェイスなし、抽象化なしなど)でユニットテストを実行しようとしています。
これは、ServletContext(Tomcatによって設定されていると想定しています)を使用するサーブレットであり、データベース情報がweb.xml /context.xmlファイルに設定されています。今、私は偽のServletContextを作成する方法を理解しましたが、コードには
_ InitialContext _ic = new InitialContext();
_
いたるところにあります(したがって、それを置き換えることは不可能です)。デフォルトのInitialContext()が例外をスローせずに_ic.lookup(val)
を実行できるようにする方法を見つける必要があります。
Context.xmlが読み込まれる方法があると思いますが、その魔法がどのように機能するのか、空白を描画しています。誰かアイデアはありますか?
InitialContext
がSPIを使用して作成を処理するという事実を利用してください。javax.naming.spi.InitialContextFactory
の実装を作成し、それをに渡すことで、ライフサイクルにフックできます。システムプロパティjavax.naming.factory.initial
(Context.INTITIAL_CONTEXT_FACTORY
)を介してテストします。思ったよりも簡単です。
このクラスを考えると:
public class UseInitialContext {
public UseInitialContext() {
try {
InitialContext ic = new InitialContext();
Object myObject = ic.lookup("myObject");
System.out.println(myObject);
} catch (NamingException e) {
e.printStackTrace();
}
}
}
そして、このInitialContextFactory
の実装:
public class MyInitialContextFactory implements InitialContextFactory {
public Context getInitialContext(Hashtable<?, ?> arg0)
throws NamingException {
Context context = Mockito.mock(Context.class);
Mockito.when(context.lookup("myObject")).thenReturn("This is my object!!");
return context;
}
}
を使用したjunitテストでUseInitialContext
のインスタンスを作成する
-Djava.naming.initial.factory=initial.context.test.MyInitialContext
コマンドラインでThis is my object!!
を出力します(Eclipseで簡単に設定できます)。私は Mockito モックとスタブが好きです。また、多くのレガシーコードを扱う場合は、Micheal Featherの レガシーコードを効果的に使用する をお勧めします。テストのために特定の部分を分離するために、プログラム内の継ぎ目を見つける方法がすべてです。
これが、単体テストの初期コンテキストを設定するための私の解決策です。まず、次のテスト依存関係をプロジェクトに追加しました。
<dependency>
<groupId>org.Apache.Tomcat</groupId>
<artifactId>catalina</artifactId>
<version>6.0.33</version>
<scope>test</scope>
</dependency>
次に、次のコードを使用して静的メソッドを作成しました。
public static void setupInitialContext() throws Exception {
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.Apache.naming.Java.javaURLContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES, "org.Apache.naming");
InitialContext ic = new InitialContext();
ic.createSubcontext("jdbc");
PGSimpleDataSource ds = new PGSimpleDataSource();
ds.setDatabaseName("postgres");
ds.setUser("postgres");
ds.setPassword("admin");
ic.bind("jdbc/something", ds);
}
最後に、各テストクラスに、setupInitialContextを呼び出す@BeforeClassメソッドを追加します。
次の前にシステム変数を設定してみてください。
System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.Apache.naming.Java.javaURLContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES,
"org.Apache.naming");
InitialContext ic = new InitialContext();
JUnitを使用している場合は、次のドキュメントに従ってください: https://blogs.Oracle.com/randystuph/entry/injecting_jndi_datasources_for_junit
今日、私は同じ問題に直面し(PowerMockを使用できません)、次のように解決しました。
コンストラクターをルックアップしないでください。オブジェクトで@InitMockを呼び出すとき、コンストラクターはまだコンテキストを必要としません。
「getService()。serviceMethod(param、param ...)」のように、必要に応じてサービスBeanを取得するためのメソッドを作成します。
/* Class ApplicationResourceProvider */ /* We can mock this and set it up with InjectMocks */ InitialContext ic; /* method hiding the lookup */ protected ApplicationService getService() throws NamingException { if(ic == null) ic = new InitialContext(); return (ApplicationService)ic.lookup("Java:global/defaultApplicationLocal"); }
@Mock ApplicationService applicationServiceBean; @Mock InitialContext ic; @InjectMocks ApplicationResourceProvider arp; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(ic.lookup(anyString())).thenReturn(applicationServiceBean); ... }
外部ライブラリを使用しない貧乏人のスタンドアロン実装:
_public class myTestClass {
public static class TestContext extends InitialContext {
public TestContext() throws NamingException {
super(true /*prevents initialization*/);
}
static Object someExpectedValue = "the expected string or object instance";
/*override the method(s) called by the legacy program on _ic, check the parameter and return the wanted value */
public Object lookup(String name) throws NamingException {
return name != null && name.equals("theValueOfVal") ? someExpectedValue : null;
}
}
public static class TestInitialContextFactory implements InitialContextFactory {
public Context getInitialContext(Hashtable<?, ?> arg0) throws NamingException {
return new TestContext();
}
}
public static void main(String[] args) throws SQLException {
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "the.package.myTestClass$TestInitialContextFactory");
/*now call the legacy logic to be tested*/
...
_
switch
メソッドのオーバーライドでlookup
を使用して、レガシープログラム全体で_ic.lookup(val)
に渡された異なるval
値ごとに期待値を返すことができます。 。
mockito を検討しましたか?
それは次のように簡単です:
InitialContext ctx = mock(InitialContext.class);
ちなみに、モックを使用することを選択した場合は、この記事も読むことをお勧めします: http://martinfowler.com/articles/mocksArentStubs.html