web-dev-qa-db-ja.com

MockitoモックをSpring beanに注入する

JUnitを使った単体テストの目的で、MockitoモックオブジェクトをSpring(3+)Beanにインジェクトしたいと思います。私のBeanの依存関係は現在、プライベートメンバフィールドに@Autowiredアノテーションを使用して注入されています。

ReflectionTestUtils.setFieldの使用を検討しましたが、注入したいBeanインスタンスは実際にはプロキシであるため、ターゲットクラスのプライベートメンバフィールドを宣言しません。私はテストの目的のためだけに私のインターフェースを修正するので、私は依存関係へのパブリックセッターを作成したくありません。

私はSpringコミュニティから与えられたいくつかの アドバイス に従っていますが、モックは作成されず、自動配線は失敗します。

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>

私が現在遭遇するエラーは以下の通りです:

...
Caused by: org...NoSuchBeanDefinitionException:
    No matching bean of type [com.package.Dao] found for dependency:
    expected at least 1 bean which qualifies as autowire candidate for this dependency.
    Dependency annotations: {
        @org...Autowired(required=true),
        @org...Qualifier(value=dao)
    }
at org...DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(D...y.Java:901)
at org...DefaultListableBeanFactory.doResolveDependency(D...y.Java:770)

constructor-arg値を無効に設定しても、アプリケーションコンテキストの起動時にエラーは発生しません。

264
teabot

最善の方法は次のとおりです。

<bean id="dao" class="org.mockito.Mockito" factory-method="mock"> 
    <constructor-arg value="com.package.Dao" /> 
</bean> 

更新
コンテキストファイルでは、このモックはそれに依存する自動配線フィールドが宣言される前にリストされなければなりません。

124
amra
@InjectMocks
private MyTestObject testObject;

@Mock
private MyDependentObject mockedObject;

@Before
public void setup() {
        MockitoAnnotations.initMocks(this);
}

これはモックされたオブジェクトをテストクラスに注入します。この場合、それはtestObjectにmockedObjectを注入します。これは上で述べたが、ここにコードがある。

106
Greg Beauchamp

Spring Java ConfigとMockitoを使った非常に簡単な解決策があります。

@Configuration
public class TestConfig {

    @Mock BeanA beanA;
    @Mock BeanB beanB;

    public TestConfig() {
        MockitoAnnotations.initMocks(this); //This is a key
    }

    //You basically generate getters and add @Bean annotation everywhere
    @Bean
    public BeanA getBeanA() {
        return beanA;
    }

    @Bean
    public BeanB getBeanB() {
        return beanB;
    }
}
62
Piotr Gwiazda

もしあなたがSpring Boot 1.4を使っているのであれば、これを行うための素晴らしい方法があります。あなたのクラスに@SpringBootTestを、フィールドに@MockBeanを使うだけで、Spring Bootはこのタイプのモックを作成し、それをコンテキストにインジェクトします(元のものをインジェクトする代わりに)

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {

    @MockBean
    private RemoteService remoteService;

    @Autowired
    private Reverser reverser;

    @Test
    public void exampleTest() {
        // RemoteService has been injected into the reverser bean
        given(this.remoteService.someCall()).willReturn("mock");
        String reverse = reverser.reverseSomeCall();
        assertThat(reverse).isEqualTo("kcom");
    }

}

一方、Spring Bootを使用していない場合、または以前のバージョンを使用している場合は、もう少し作業が必要です。

あなたのモックをSpringコンテキストにインジェクトする@Configurationビーンを作成してください:

@Configuration
@Profile("useMocks")
public class MockConfigurer {

    @Bean
    @Primary
    public MyBean myBeanSpy() {
        return mock(MyBean.class);
    }
}

@Primaryアノテーションを使用して、修飾子が指定されていない場合、このBeanが優先されることをSpringに伝えています。

どのクラスがモックを使用し、どのクラスが本物のBeanを使用するかを制御するために、必ず@Profile("useMocks")でクラスに注釈を付けてください。

最後に、テストでuserMocksプロファイルを有効にします。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
@ActiveProfiles(profiles={"useMocks"})
public class YourIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the mock!


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

モックではなく本物のBeanを使用したくない場合は、useMocks profileをアクティブにしないでください。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
public class AnotherIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the real implementation!


    @Test
    public void test() {
        ....
    }
}
34
jfcorugedo

1.8. Mockitoには@InjectMocksがあります - これは非常に便利です。私のJUnitテストはMockitoJUnitRunnerと@Runです。テスト対象クラスのすべての依存関係を満たす@Mockオブジェクトを作成します。これらはすべて、プライベートメンバーに@InjectMocksのアノテーションが付けられたときに注入されます。

SpringJUnit4Runnerとの統合テストは今だけです。

私はそれがSpringと同じ方法でListを注入することができるようには思えないことに注意するでしょう。それはListを満たすMockオブジェクトだけを探し、Mockオブジェクトのリストを挿入しません。私のための回避策は手動でインスタンス化されたリストに対して@Spyを使い、ユニットテストのためにそのリストに手動でモックオブジェクトを追加することでした。それが意図的なものだったのかもしれません。

19
Doug Moscrop

更新:この問題に対するより良い、よりクリーンな解決策があります。最初に他の答えを検討してください。

私は最終的に彼のブログでronenによってこれに対する答えを見つけました。私が抱えていた問題は、メソッドMockito.mock(Class c)が戻り値の型Objectを宣言していることにあります。その結果、Springはファクトリメソッドの戻り値の型からBeanの型を推測できません。

Ronenの解決策 はモックを返すFactoryBeanの実装を作成することです。 FactoryBeanインタフェースを使用すると、SpringはファクトリBeanによって作成されたオブジェクトの種類を照会できます。

私のモックされたbeanの定義は今のようになります:

<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>
13
teabot

Spring 3.2では、これはもう問題ではありません。 Springは一般的なファクトリメソッドの結果の自動配線をサポートします。このブログ記事の「汎用ファクトリメソッド」というセクションを参照してください。 http://spring.io/blog/2012/11/07/spring-framework-3-2-rc1-new -testing-features /

重要な点は次のとおりです。

Spring 3.2では、ファクトリメソッドの一般的な戻り型は正しく推論され、モックのための型による自動配線は期待通りに動作するはずです。その結果、MockitoFactoryBean、EasyMockFactoryBean、またはSpringockitoなどのカスタム回避策は不要になる可能性があります。

つまりこれは箱から出して動作するはずです。

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>
11
Ryan Walls

spring> = 3.0を使用している場合は、Springsの@Configurationアノテーションを使用してアプリケーションコンテキストの一部を定義してください。

@Configuration
@ImportResource("com/blah/blurk/rest-of-config.xml")
public class DaoTestConfiguration {

    @Bean
    public ApplicationService applicationService() {
        return mock(ApplicationService.class);
    }

}

@ImportResourceを使用したくない場合は、逆の方法でも実行できます。

<beans>
    <!-- rest of your config -->

    <!-- the container recognize this as a Configuration and adds it's beans 
         to the container -->
    <bean class="com.package.DaoTestConfiguration"/>
</beans>

詳しくは、spring-framework-referenceを参照してください。 Javaベースのコンテナー構成

9
Markus T

以下のコードは自動配線で動作します - これは最短のバージョンではありませんが、標準のスプリング/モッキトジャーでのみ動作するはずの場合に役立ちます。

<bean id="dao" class="org.springframework.aop.framework.ProxyFactoryBean">
   <property name="target"> <bean class="org.mockito.Mockito" factory-method="mock"> <constructor-arg value="com.package.Dao" /> </bean> </property>
   <property name="proxyInterfaces"> <value>com.package.Dao</value> </property>
</bean> 
9
Kamil

おそらく完璧な解決策ではありませんが、私は単体テストのためにDIを行うために春を使わない傾向があります。単一のBean(テスト対象のクラス)に対する依存関係は、通常、それほど複雑ではありません。そのため、テストコード内で直接注入を行います。

7
Angelo Genovese

Mockitoを使って次のことができます。

<bean id="stateMachine" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.abcd.StateMachine"/>
</bean>
7
Alexander

上記のアプローチに基づいていくつかの例を投稿する

春と:

@ContextConfiguration(locations = { "classpath:context.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class TestServiceTest {
    @InjectMocks
    private TestService testService;
    @Mock
    private TestService2 testService2;
}

春がなければ:

@RunWith(MockitoJUnitRunner.class)
public class TestServiceTest {
    @InjectMocks
    private TestService testService = new TestServiceImpl();
    @Mock
    private TestService2 testService2;
}
6
Basu

更新 - 新しい答えはこちら: https://stackoverflow.com/a/19454282/411229 。この答えは、3.2より前のバージョンのSpringにのみ当てはまります。

私はこれに対するもっと決定的な解決策をしばらく探していました。このブログ記事は私のすべてのニーズを網羅しているようで、Bean宣言の順序付けには依存しません。すべてのMattias Seversonの功績による。 http://www.jayway.com/2011/11/30/spring-integration-tests-part-i-creating-mock-objects/

基本的には、FactoryBeanを実装する

package com.jayway.springmock;

import org.mockito.Mockito;
import org.springframework.beans.factory.FactoryBean;

/**
 * A {@link FactoryBean} for creating mocked beans based on Mockito so that they 
 * can be {@link @Autowired} into Spring test configurations.
 *
 * @author Mattias Severson, Jayway
 *
 * @see FactoryBean
 * @see org.mockito.Mockito
 */
public class MockitoFactoryBean<T> implements FactoryBean<T> {

    private Class<T> classToBeMocked;

    /**
     * Creates a Mockito mock instance of the provided class.
     * @param classToBeMocked The class to be mocked.
     */
    public MockitoFactoryBean(Class<T> classToBeMocked) {
        this.classToBeMocked = classToBeMocked;
    }

    @Override
    public T getObject() throws Exception {
        return Mockito.mock(classToBeMocked);
    }

    @Override
    public Class<?> getObjectType() {
        return classToBeMocked;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

次に、次のようにしてSpringの設定を更新します。

<beans...>
    <context:component-scan base-package="com.jayway.example"/>

    <bean id="someDependencyMock" class="com.jayway.springmock.MockitoFactoryBean">
        <constructor-arg name="classToBeMocked" value="com.jayway.example.SomeDependency" />
    </bean>
</beans>
2
Ryan Walls

Springockitoの開発ペースオープンイシューの数 を見てみると、最近私のテストスイートスタックにそれを導入するのは少し心配です。前回のリリースがSpring 4のリリースより前に行われたという事実は、「Spring 4と簡単に統合することは可能ですか?」のような疑問を投げかけます。私は試していないので、わかりません。統合テストでSpring Beanをモックする必要がある場合は、純粋なSpringアプローチを使用します。

単純なSpring機能を備えたSpring Beanを偽造するオプションがあります。それには@Primary@Profileおよび@ActiveProfilesアノテーションを使用する必要があります。 私はこのトピックに関するブログ記事を書きました。

2
luboskrnac

プロジェクトをSpring Boot 1.4に移行することをお勧めします。その後、新しいアノテーション @MockBean を使ってあなたのcom.package.Daoを偽造することができます

1
luboskrnac

私はKresimir Nesekの提案に基づいてソリューションを開発しました。コードをもう少しわかりやすくモジュール化するために、新しい注釈@ EnableMockedBeanを追加しました。

@EnableMockedBean
@SpringBootApplication
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes=MockedBeanTest.class)
public class MockedBeanTest {

    @MockedBean
    private HelloWorldService helloWorldService;

    @Autowired
    private MiddleComponent middleComponent;

    @Test
    public void helloWorldIsCalledOnlyOnce() {

        middleComponent.getHelloMessage();

        // THEN HelloWorldService is called only once
        verify(helloWorldService, times(1)).getHelloMessage();
    }

}

それを説明した post を書きました。

1
Alfredo Diaz

Markus Tの回答で使用されたアプローチと、どのクラスをモックするかを指定できるカスタムアノテーション(@MockedBeans)を探す単純なヘルパー実装のImportBeanDefinitionRegistrarを組み合わせて使用​​します。このアプローチでは、モッキングに関連する定型コードの一部を削除した簡潔な単体テストになると思います。

サンプルの単体テストがこの方法でどのように見えるかを次に示します。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class ExampleServiceIntegrationTest {

    //our service under test, with mocked dependencies injected
    @Autowired
    ExampleService exampleService;

    //we can autowire mocked beans if we need to used them in tests
    @Autowired
    DependencyBeanA dependencyBeanA;

    @Test
    public void testSomeMethod() {
        ...
        exampleService.someMethod();
        ...
        verify(dependencyBeanA, times(1)).someDependencyMethod();
    }

    /**
     * Inner class configuration object for this test. Spring will read it thanks to
     * @ContextConfiguration(loader=AnnotationConfigContextLoader.class) annotation on the test class.
     */
    @Configuration
    @Import(TestAppConfig.class) //TestAppConfig may contain some common integration testing configuration
    @MockedBeans({DependencyBeanA.class, DependencyBeanB.class, AnotherDependency.class}) //Beans to be mocked
    static class ContextConfiguration {

        @Bean
        public ExampleService exampleService() {
            return new ExampleService(); //our service under test
        }
    }
}

これを実現するには、カスタムアノテーション(@MockedBeans)とカスタムImportBeanDefinitionRegistrarの実装の2つの単純なヘルパークラスを定義する必要があります。 @MockedBeansアノテーション定義は@Import(CustomImportBeanDefinitionRegistrar.class)でアノテーションを付ける必要があり、ImportBeanDefinitionRgistrarはモックされたBean定義をそのregisterBeanDefinitionsメソッド内の構成に追加する必要があります。

このアプローチが好きなら、sample implementation on my --- blogpost を見つけることができます。

1
Kresimir Nesek

私はモックを提供するMockFactoryを作成するためのティーボットと同じような答えを見つけました。私は次の例を使ってモックファクトリを作成しました(narkisrへのリンクが切れているので): http://hg.randompage.org/Java/src/407e78aa08a0/projects/bookmarking/backend/spring/ src/test/Java/org /ランダムページ/ブックマーク/バックエンド/ testUtils/MocksFactory.Java

<bean id="someFacade" class="nl.package.test.MockFactory">
    <property name="type" value="nl.package.someFacade"/>
</bean>

これは、Springがモックビーンからのインジェクションを解決しようとしていることを防ぐのにも役立ちます。

1
Renso Lohuis
<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>

これは、XMLファイルの最初または早い段階で宣言すれば、完全にうまく機能します。 Mockito 1.9.0/Spring 3.0.5

1

今日、私はMockitoの豆の前に宣言した春の文脈がロードに失敗していることを知りました。モックの後に移動した後、アプリコンテキストは正常にロードされました。世話をする :)

0

記録としては、私のテストはすべてフィクスチャを遅延初期化するだけで正しく動作します。

<bean id="fixture"
      class="it.tidalwave.northernwind.rca.embeddedserver.impl.DefaultEmbeddedServer"
      lazy-init="true" /> <!-- To solve Mockito + Spring problems -->

<bean class="it.tidalwave.messagebus.aspect.spring.MessageBusAdapterFactory" />

<bean id="applicationMessageBus"
      class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="it.tidalwave.messagebus.MessageBus" />
</bean>

<bean class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="javax.servlet.ServletContext" />
</bean>

理論的根拠はMattiasが説明するものだと思います ここの (記事の最後にあります)回避策はbeanが宣言される順序を変更することです - 遅延初期化はフィクスチャがで宣言されることの "一種"です終わり。

0