テストしたいSpringコンポーネントがあり、このコンポーネントには自動テストされた属性があり、ユニットテストの目的で変更する必要があります。問題は、クラスがpost-constructメソッド内でautowiredコンポーネントを使用するため、実際に使用される前に(ReflectionTestUtilsを介して)置き換えることができないことです。
どうすればいいですか?
これは私がテストしたいクラスです:
@Component
public final class TestedClass{
@Autowired
private Resource resource;
@PostConstruct
private void init(){
//I need this to return different result
resource.getSomething();
}
}
そして、これがテストケースのベースです:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= "classpath:applicationContext.xml")
public class TestedClassTest{
@Autowired
private TestedClass instance;
@Before
private void setUp(){
//this doesn't work because it's executed after the bean is instantiated
ReflectionTestUtils.setField(instance, "resource", new Resource("something"));
}
}
Postconstructメソッドが呼び出される前にリソースを別のものに置き換える方法はありますか? Spring JUnitランナーに異なるインスタンスを自動配線するように指示したいですか?
定義する@Autowired
Beanがテストに必要なタイプである新しいtestContext.xmlを提供できます。
Mockito を使用できます。 PostConstruct
とは具体的にはわかりませんが、これは一般に機能します:
// Create a mock of Resource to change its behaviour for testing
@Mock
private Resource resource;
// Testing instance, mocked `resource` should be injected here
@InjectMocks
@Resource
private TestedClass testedClass;
@Before
public void setUp() throws Exception {
// Initialize mocks created above
MockitoAnnotations.initMocks(this);
// Change behaviour of `resource`
when(resource.getSomething()).thenReturn("Foo");
}
Spring Boot 1.4では、 @MockBean
と呼ばれるテストアノテーションが導入されました。そのため、Spring Beanのモックとスパイは、Spring Bootによってネイティブにサポートされています。
トピックに関するブログ投稿 を作成しました。また、Githubリポジトリへのリンクと実際の例も含まれています。
トリックは、元のSpring Beanを偽のSpring Beanでオーバーライドするテスト構成を使用することです。このトリックには、@Primary
および@Profile
注釈を使用できます。
Spring-reinjectでモックでBean定義をオーバーライドできます https://github.com/sgri/spring-reinject/
統合テストの別のアプローチは、新しい構成クラスを定義し、@ContextConfiguration
として提供することです。この構成では、Beanをモックできます。また、テスト/フローで使用しているすべてのタイプのBeanを定義する必要があります。例を提供するには:
@RunWith(SpringRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class MockTest{
@Configuration
static class ContextConfiguration{
// ... you beans here used in test flow
@Bean
public MockMvc mockMvc() {
return MockMvcBuilders.standaloneSetup(/*you can declare your controller beans defines on top*/)
.addFilters(/*optionally filters*/).build();
}
//Defined a mocked bean
@Bean
public MyService myMockedService() {
return Mockito.mock(MyService.class);
}
}
@Autowired
private MockMvc mockMvc;
@Autowired
MyService myMockedService;
@Before
public void setup(){
//mock your methods from MyService bean
when(myMockedService.myMethod(/*params*/)).thenReturn(/*my answer*/);
}
@Test
public void test(){
//test your controller which trigger the method from MyService
MvcResult result = mockMvc.perform(get(CONTROLLER_URL)).andReturn();
// do your asserts to verify
}
}