76

我有一个要测试的 Spring 组件,并且该组件有一个自动装配的属性,我需要更改它以进行单元测试。问题是,该类在 post-construct 方法中使用自动装配组件,因此在实际使用它之前我无法替换它(即通过 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 runner 自动装配不同的实例吗?

4

6 回答 6

65

你可以使用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");   
}
于 2013-10-10T15:47:36.907 回答
26

Spring Boot 1.4 引入了名为@MockBean. 所以现在 Spring Boot 原生支持对 Spring bean 的模拟和监视。

于 2016-09-05T10:42:54.687 回答
13

您可以提供一个新的 testContext.xml,其中@Autowired您定义的 bean 是您的测试所需的类型。

于 2013-10-10T15:24:01.783 回答
7

我创建了关于该主题的博客文章。它还包含指向带有工作示例的 Github 存储库的链接。

诀窍是使用测试配置,在其中你用假的覆盖原始的spring bean。您可以使用@Primary@Profile注释这个技巧。

于 2016-05-05T12:51:30.543 回答
6

您可以使用 spring-reinject https://github.com/sgri/spring-reinject/模拟覆盖 bean 定义

于 2014-02-02T15:24:10.490 回答
4

集成测试中的另一种方法是定义一个新的 Configuration 类并将其作为您的@ContextConfiguration. 在配置中,您将能够模拟您的 bean,并且您必须定义您在 test/s 流中使用的所有类型的 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
 }
}
于 2018-10-26T06:38:27.687 回答