web-dev-qa-db-ja.com

本番環境でCDIを使用してクラスをテストするときにモックを注入する方法

私はJava SE環境で依存関係注入にWELD-SEを使用してプログラミングしています。したがって、クラスの依存関係は次のようになります。

_public class ProductionCodeClass {
    @Inject
    private DependencyClass dependency;
}
_

このクラスの単体テストを作成するときは、DependencyClassのモックを作成しています。実行するすべてのテストで完全なCDI環境を開始したくないので、モックを手動で「挿入」します。

_import static TestSupport.setField;
import static org.mockito.Mockito.*;

public class ProductionCodeClassTest {
    @Before
    public void setUp() {
        mockedDependency = mock(DependencyClass.class);
        testedInstance = new ProductionCodeClass();
        setField(testedInstance, "dependency", mockedDependency);
    }
}
_

静的にインポートされたメソッドsetField()テストで使用するツールを使用して、クラスに自分自身を記述しました。

_public class TestSupport {
    public static void setField(
                                final Object instance,
                                final String field,
                                final Object value) {
        try {
            for (Class classIterator = instance.getClass();
                 classIterator != null;
                 classIterator = classIterator.getSuperclass()) {
                try {
                    final Field declaredField =
                                classIterator.getDeclaredField(field);
                    declaredField.setAccessible(true);
                    declaredField.set(instance, value);
                    return;
                } catch (final NoSuchFieldException nsfe) {
                    // ignored, we'll try the parent
                }
            }

            throw new NoSuchFieldException(
                      String.format(
                          "Field '%s' not found in %s",
                          field,
                          instance));
        } catch (final RuntimeException re) {
            throw re;
        } catch (final Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}
_

このソリューションについて私が気に入らないのは、新しいプロジェクトで何度もこのヘルパーが必要になることです。私はすでにそれをテスト依存としてプロジェクトに追加できるMavenプロジェクトとしてパッケージ化しました。

しかし、他に欠けている一般的なライブラリで作成されたものはありませんか?これを行う一般的な方法についてのコメントはありますか?

15
Matthias Wimmer

Mockitoはすぐにこれをサポートします。

_public class ProductionCodeClassTest {

    @Mock
    private DependencyClass dependency;

    @InjectMocks
    private ProductionCodeClass testedInstance;

    @Before
    public void setUp() {
        testedInstance = new ProductionCodeClass();
        MockitoAnnotations.initMocks(this);
    }

}
_

_@InjectMocks_ アノテーションは、テストクラスでモックされたクラスまたはインターフェースの注入をトリガーします。この場合はDependencyClass

Mockitoはタイプごとに注入しようとします(タイプが同じ場合は名前を使用します)。インジェクションが失敗しても、Mockitoは何もスローしません。依存関係を手動で満たす必要があります。

ここでは、mock()を呼び出す代わりに、_@Mock_アノテーションも使用しています。 mock()を使用することもできますが、アノテーションを使用することをお勧めします。

補足として、TestSupportに実装した機能をサポートするリフレクションツールがあります。そのような例の1つは ReflectionTestUtils です。


おそらくより良いのは コンストラクタインジェクション を使用することです:

_public class ProductionCodeClass {

    private final DependencyClass dependency;

    @Inject
    public ProductionCodeClass(DependencyClass dependency) {
        this.dependency = dependency;
    }
}
_

ここでの主な利点は、クラスが依存するクラスが明確であり、すべての依存関係を提供しないと簡単に構築できないことです。また、注入されたクラスをfinalにすることもできます。

これにより、_@InjectMocks_は不要になります。代わりに、コンストラクターのパラメーターとしてモックを提供して、クラスを作成します。

_public class ProductionCodeClassTest {

    @Mock
    private DependencyClass dependency;

    private ProductionCodeClass testedInstance;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        testedInstance = new ProductionCodeClass(dependency);
    }

}
_
19
Magnilex

Mockitosの組み込み関数では不十分な場合の代替策:試してみてください needle4j.org

これは、モックと具象インスタンスのインジェクションを可能にし、ライフサイクルシミュレーションのpostConstructもサポートするインジェクション/モックフレームワークです。

 public class ProductionCodeClassTest {

    @Rule
    public final NeedleRule needle = new NeedleRule();

    // will create productionCodeClass and inject mocks by default
    @ObjectUnderTest(postConstruct=true)
    private ProductionCodeClass testedInstance;

    // this will automatically be a mock
    @Inject
    private AServiceProductionCodeClassDependsOn serviceMock;

    // this will be injected into ObjectUnderTest 
    @InjectIntoMany
    private ThisIsAnotherDependencyOfProdcutionCodeClass realObject = new ThisIsAnotherDependencyOfProdcutionCodeClass ();

    @Test
    public void test_stuff() {
         ....
    }

}
2
Jan Galinski