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:如何在自动装配组件中模拟自动装配组件的主要内容,如果未能解决你的问题,请参考以下文章