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
値を無効に設定しても、アプリケーションコンテキストの起動時にエラーは発生しません。
最善の方法は次のとおりです。
<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.package.Dao" />
</bean>
更新
コンテキストファイルでは、このモックはそれに依存する自動配線フィールドが宣言される前にリストされなければなりません。
@InjectMocks
private MyTestObject testObject;
@Mock
private MyDependentObject mockedObject;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
これはモックされたオブジェクトをテストクラスに注入します。この場合、それはtestObjectにmockedObjectを注入します。これは上で述べたが、ここにコードがある。
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;
}
}
もしあなたが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() {
....
}
}
1.8. Mockitoには@InjectMocksがあります - これは非常に便利です。私のJUnitテストはMockitoJUnitRunnerと@Runです。テスト対象クラスのすべての依存関係を満たす@Mockオブジェクトを作成します。これらはすべて、プライベートメンバーに@InjectMocksのアノテーションが付けられたときに注入されます。
SpringJUnit4Runnerとの統合テストは今だけです。
私はそれがSpringと同じ方法でListを注入することができるようには思えないことに注意するでしょう。それはListを満たすMockオブジェクトだけを探し、Mockオブジェクトのリストを挿入しません。私のための回避策は手動でインスタンス化されたリストに対して@Spyを使い、ユニットテストのためにそのリストに手動でモックオブジェクトを追加することでした。それが意図的なものだったのかもしれません。
更新:この問題に対するより良い、よりクリーンな解決策があります。最初に他の答えを検討してください。
私は最終的に彼のブログで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>
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>
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ベースのコンテナー構成
以下のコードは自動配線で動作します - これは最短のバージョンではありませんが、標準のスプリング/モッキトジャーでのみ動作するはずの場合に役立ちます。
<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>
おそらく完璧な解決策ではありませんが、私は単体テストのためにDIを行うために春を使わない傾向があります。単一のBean(テスト対象のクラス)に対する依存関係は、通常、それほど複雑ではありません。そのため、テストコード内で直接注入を行います。
Mockitoを使って次のことができます。
<bean id="stateMachine" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.abcd.StateMachine"/>
</bean>
上記のアプローチに基づいていくつかの例を投稿する
春と:
@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;
}
更新 - 新しい答えはこちら: 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>
Springockitoの開発ペース と オープンイシューの数 を見てみると、最近私のテストスイートスタックにそれを導入するのは少し心配です。前回のリリースがSpring 4のリリースより前に行われたという事実は、「Spring 4と簡単に統合することは可能ですか?」のような疑問を投げかけます。私は試していないので、わかりません。統合テストでSpring Beanをモックする必要がある場合は、純粋なSpringアプローチを使用します。
単純なSpring機能を備えたSpring Beanを偽造するオプションがあります。それには@Primary
、@Profile
および@ActiveProfiles
アノテーションを使用する必要があります。 私はこのトピックに関するブログ記事を書きました。
プロジェクトをSpring Boot 1.4に移行することをお勧めします。その後、新しいアノテーション @MockBean
を使ってあなたのcom.package.Dao
を偽造することができます
私は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 を書きました。
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 を見つけることができます。
私はモックを提供する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がモックビーンからのインジェクションを解決しようとしていることを防ぐのにも役立ちます。
<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
今日、私はMockitoの豆の前に宣言した春の文脈がロードに失敗していることを知りました。モックの後に移動した後、アプリコンテキストは正常にロードされました。世話をする :)
記録としては、私のテストはすべてフィクスチャを遅延初期化するだけで正しく動作します。
<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が宣言される順序を変更することです - 遅延初期化はフィクスチャがで宣言されることの "一種"です終わり。