Java 模拟服务/组件的Spring引导集成测试
情况和问题:在Spring Boot中,如何向应用程序中注入一个或多个模拟类/bean来进行集成测试?有一些关于StackOverflow的答案,但是他们关注的是Spring Boot 1.4之前的情况,或者根本不适合我 背景是,在下面的代码中,设置的实现依赖于第三方服务器和其他外部系统。设置的功能已经在单元测试中进行了测试,所以对于完整的集成测试,我想模拟对这些服务器或系统的依赖关系,只提供虚拟值 MockBean将忽略所有现有的bean定义,并提供一个虚拟对象,但该对象在注入该类的其他类中不提供方法行为。在测试之前使用@Before方法设置行为不会影响注入的对象,或者不会注入到其他应用程序服务(如AuthenticationService)中 我的问题:如何将bean注入到应用程序上下文中? 我的测试:Java 模拟服务/组件的Spring引导集成测试,java,spring-boot,junit4,spring-test,Java,Spring Boot,Junit4,Spring Test,情况和问题:在Spring Boot中,如何向应用程序中注入一个或多个模拟类/bean来进行集成测试?有一些关于StackOverflow的答案,但是他们关注的是Spring Boot 1.4之前的情况,或者根本不适合我 背景是,在下面的代码中,设置的实现依赖于第三方服务器和其他外部系统。设置的功能已经在单元测试中进行了测试,所以对于完整的集成测试,我想模拟对这些服务器或系统的依赖关系,只提供虚拟值 MockBean将忽略所有现有的bean定义,并提供一个虚拟对象,但该对象在注入该类的其他类中不
package ch.swaechter.testapp;
import ch.swaechter.testapp.utils.settings.Settings;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.junit4.SpringRunner;
@TestConfiguration
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MyApplication.class})
public class MyApplicationTests {
@MockBean
private Settings settings;
@Before
public void before() {
Mockito.when(settings.getApplicationSecret()).thenReturn("Application Secret");
}
@Test
public void contextLoads() {
String applicationsecret = settings.getApplicationSecret();
System.out.println("Application secret: " + applicationsecret);
}
}
下面是一个应该使用模拟类但不接收该模拟类的服务:
package ch.swaechter.testapp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AuthenticationServiceImpl implements AuthenticationService {
private final Settings settings;
@Autowired
public AuthenticationServiceImpl(Settings settings) {
this.settings = settings;
}
@Override
public boolean loginUser(String token) {
// Use the application secret to check the token signature
// But here settings.getApplicationSecret() will return null (Instead of Application Secret as specified in the mock)!
return false;
}
}
当您使用@MockBean注释一个字段时,spring将创建一个注释类的mock,并使用它自动连接应用程序上下文的所有bean 你必须而不是用
Settings settings = Mockito.mock(Settings.class);
这将创建第二个模拟,导致所描述的问题
解决方案:
@MockBean
private Settings settings;
@Before
public void before() {
Mockito.when(settings.getApplicationSecret()).thenReturn("Application Secret");
}
@Test
public void contextLoads() {
String applicationsecret = settings.getApplicationSecret();
System.out.println("Application secret: " + applicationsecret);
}
看起来您正在使用设置对象,然后才指定其模拟行为。 你必须跑
Mockito.when(settings.getApplicationSecret()).thenReturn("Application Secret");
在配置设置期间。为了防止这种情况,您可以仅为测试创建特殊配置类
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MyApplication.class, MyApplicationTest.TestConfig.class})
public class MyApplicationTest {
private static final String SECRET = "Application Secret";
@TestConfiguration
public static class TestConfig {
@Bean
@Primary
public Settings settingsBean(){
Settings settings = Mockito.mock(Settings.class);
Mockito.when(settings.getApplicationSecret()).thenReturn(SECRET);
Mockito.doReturn(SECRET).when(settings).getApplicationSecret();
return settings;
}
}
.....
}
此外,我建议您使用下一个符号进行模拟:
Mockito.doReturn(SECRET).when(settings).getApplicationSecret();
它不会运行settings::getApplicationSecret将您曾经拥有的移动到
@之前的方法中
,因为spring测试不会调用@Bean
,如果您需要调用您的Bean,请使用@testconfiguration
注释类,并创建模拟服务感谢您的回答!这将适用于contextLoads测试中的settings对象(值为“Application Secret”)。但是,对于应用程序中需要autowire设置的所有其他组件,将使用没有方法定义的默认模拟对象。有没有办法也在那里注入模拟对象?遗憾的是,这并不能解决我的问题,所以我只是更新了我的问题,以更好地反映我的问题。我只是意识到我从未接受过这个答案。非常感谢。