Spring JUnit:如何在自动装配的组件中模拟自动装配的组件


68

我有一个要测试的Spring组件,该组件具有autowired属性,出于单元测试的目的,我需要更改该属性。问题是,该类在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运行程序自动连接其他实例吗?


取决于您使用的Spring版本-可能会有不同的答案。请参阅:dzone.com/articles/mockbean-spring-boots-missing-ingredient
Witold Kaczurba,

Answers:


11

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


1
谢谢。那正是我所做的并且正在工作。我只是想知道是否有一种方法可以不编写新的上下文(尽管我只导入了旧的上下文并覆盖了我需要的bean)
NeplatnyUdaj13年

1
@NeplatnyUdaj尝试使用@Qualifier(“ myAlternateBean”)然后,它应该可以解决您的问题
2013年

那是个误会。您的建议有效。这不完全是我想要的。我会用它直到找到其他东西。谢谢
NeplatnyUdaj

如果您在测试中使用自动装配,那么具有测试特定的上下文是正常的,因此我不必担心寻找更好的东西。唯一的其他可能性是重写init(这意味着它不能是私有的)或手动将bean连接进行测试。
马特·海里维尔,

@ph。在没有.xml文件的Spring-Boot中如何做。我必须为测试创建一个配置文件吗?
塔伦·马甘蒂

57

您可以使用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");   
}

1
我已经尝试过了,但是PostConstruct仍然在setUp()方法之前执行。
NeplatnyUdaj

@NeplatnyUdaj,另一个机会是@BeforeClass而不是@BeforeinitMocks虽然不确定如何使用。
安娜·祖本科

这也是我的想法,但是MockitoAnnotations.initMocks(Object o)需要测试类的实例。
NeplatnyUdaj 2013年


7

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

诀窍是使用测试配置,在该配置中,您使用伪造的Spring Bean覆盖了原始Spring Bean。您可以为此技巧使用@Primary@Profile注释。


1
谢谢,这正是我所需要的!
Daniel Sass


4

集成测试中的另一种方法是定义一个新的Configuration类,并将其作为您的@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
 }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.