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

Posted

技术标签:

【中文标题】Spring JUnit:如何在自动装配组件中模拟自动装配组件【英文标题】:Spring JUnit: How to Mock autowired component in autowired component 【发布时间】:2013-10-18 10:25:24 【问题描述】:

我有一个我想测试的 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 自动装配不同的实例?

【问题讨论】:

取决于您使用的 Spring 版本 - 可能会有不同的答案。参考:dzone.com/articles/mockbean-spring-boots-missing-ingredient 【参考方案1】:

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

【讨论】:

我已经尝试过了,但是PostConstruct 仍然在setUp() 方法之前执行。 @NeplatnyUdaj,另一个机会是@BeforeClass 而不是@Before。不知道 initMocks 是如何与这个一起工作的。 这也是我的想法,但MockitoAnnotations.initMocks(Object o) 需要一个测试类的实例。 或者使用新的 TestUnit5 在你的测试类之前使用@ExtendWith(MockitoExtension.class)【参考方案2】:

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

【讨论】:

【参考方案3】:

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

【讨论】:

谢谢。这正是我所做的并且正在工作。我只是想知道是否有办法在不编写新上下文的情况下做到这一点(尽管我只导入了旧的并覆盖了我需要的 bean) @NeplatnyUdaj 尝试使用 @Qualifier("myAlternateBean") 然后,它应该可以解决您的问题 这是个误会。你的建议有效。这不是我想要的。我会一直使用它,直到我找到别的东西。谢谢 如果你在测试中使用自动装配,那么有测试特定的上下文是正常的,所以我不会担心寻找更好的东西。唯一的其他可能性是覆盖 init(这意味着它不能是私有的)或手动连接您的 bean 以进行测试。 @ph。如何在没有 .xml 文件的 Spring-Boot 中执行此操作。我必须为测试创建配置文件吗?【参考方案4】:

我创建了blog post on the topic。它还包含指向带有工作示例的 Github 存储库的链接。

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

【讨论】:

谢谢,这正是我需要的!【参考方案5】:

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

【讨论】:

【参考方案6】:

集成测试的另一种方法是定义一个新的配置类并将其作为您的@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
 

【讨论】:

以上是关于Spring JUnit:如何在自动装配组件中模拟自动装配组件的主要内容,如果未能解决你的问题,请参考以下文章

在Camel JUnit中自动装配Spring Bean

Spring:在Junit中加载的类中自动装配不同的类

Spring 3 和 JUnit 4(自动装配)

Spring Boot 自动装配配置类进入 Junit 测试

从 JUnit 运行时 Spring 不会自动装配

如何使用 Mockito 在 Spring 中模拟自动装配的 @Value 字段?