14

全て、

古語のJavaコード(インターフェースなし、抽象化なしなど)でユニットテストを実行しようとしています。

これは、ServletContext(Tomcatによって設定されていると想定しています)を使用するサーブレットであり、データベース情報がweb.xml/context.xmlファイルに設定されています。今、私は偽のServletContextを作成する方法を理解しましたが、コードには

 InitialContext _ic = new InitialContext();

いたるところにあります(したがって、それを交換することは不可能です)。デフォルトのInitialContext()が_ic.lookup(val)例外をスローせずに実行できるようにする方法を見つける必要があります。

context.xmlが読み込まれる方法があると思いますが、その魔法がどのように機能するか、空白を描画しています。誰かアイデアはありますか?

4

7 に答える 7

37

InitialContextSPIを使用してその作成を処理するという事実を利用してください。の実装を作成し、それをシステムプロパティ( )javax.naming.spi.InitialContextFactoryを介してテストに渡すことで、そのライフサイクルにフックできます。思ったより簡単です。javax.naming.factory.initialContext.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();
        }
    }


} 

そして、このimpl of 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;
    }
}

UseInitialContextを使用してjunitテストでのインスタンスを作成する

-Djava.naming.initial.factory=initial.context.test.MyInitialContext

コマンドライン出力This is my object!!で(Eclipseで簡単に設定できます)。私はモックとスタブのためにMockitoが好きです。また、多くのレガシーコードを扱う場合は、MichealFeatherのレガシーコードを効果的に使用することをお勧めします。テストのために特定の部分を分離するために、プログラムで継ぎ目を見つける方法がすべてです。

于 2012-04-06T18:10:09.450 に答える
6

これが、単体テストの初期コンテキストを設定するための私の解決策です。最初に、次のテスト依存関係をプロジェクトに追加しました。

<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メソッドを追加します。

于 2012-04-06T16:14:32.863 に答える
4

次の前にシステム変数を設定してみてください。

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

于 2012-04-06T16:04:59.510 に答える
3

PowerMockを使用して、InitialContextの構築をモックし、その動作を制御できます。コンストラクターのモックはここに文書化されています。

PowerMockテストは非常に面倒で複雑になる可能性があり、通常はリファクタリングの方が適しています。

于 2012-04-06T15:33:26.667 に答える
1

今日、私は同じ問題に直面し(PowerMockを使用できません)、次のように解決しました。

  1. コンストラクターを検索しないでください。オブジェクトに対して@InitMockを呼び出す場合、コンストラクターはまだコンテキストを必要としません。

  2. 「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");
    }
  1. テストでは、次のように設定します。
@Mock
ApplicationService applicationServiceBean;

@Mock
InitialContext ic;

@InjectMocks
ApplicationResourceProvider arp;

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
    when(ic.lookup(anyString())).thenReturn(applicationServiceBean);
    ...
}
于 2016-11-04T16:31:20.387 に答える
0

モッキートを検討しましたか?

それは次のように簡単です:

InitialContext ctx = mock(InitialContext.class);

ちなみに、モックを使用することを選択した場合は、この記事も読むことをお勧めします:http: //martinfowler.com/articles/mocksArentStubs.html

于 2012-04-06T16:22:31.390 に答える
0

外部ライブラリを使用しない貧乏人のスタンドアロン実装:

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メソッドのオーバーライドでaを使用して、レガシープログラム全体に渡されるlookupさまざまな値ごとに期待値を返すことができます。val_ic.lookup(val)

于 2019-10-03T14:29:57.493 に答える