[英]How to test multiple Spring Boot applications in one test?
我有一个带有 2 个 Spring 引导应用程序的多模块 Maven 项目
父母
如何设置测试,您可以在同一过程中加载单独的 spring 引导应用程序,每个应用程序都有自己的配置上下文。
public abstract class AbstractIntegrationTest {//test module
protected FOO foo;
protected BAR bar;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@IntegrationTest
@Transactional
@SpringApplicationConfiguration(classes = foo.Application.class)
public class FOO {
public MockMvc mockMvc;
@Autowired
public WebApplicationContext wac;
@Before
public void _0_setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
TestCase.assertNotNull(mockMvc);
}
public void login(String username) {
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@IntegrationTest
@Transactional
@SpringApplicationConfiguration(classes = bar.Application.class)
public class BAR {
@Autowired
public WebApplicationContext wac;
public MockMvc restMvc;
@Before
public void _0_setup() {
MockitoAnnotations.initMocks(this);
restMvc = MockMvcBuilders.webAppContextSetup(wac).build();
TestCase.assertNotNull(restMvc);
}
public void login(String username) {
}
}
@Before
public void _0_setup() {
foo = new FOO();
bar = new BAR();
}
}
和一个集成测试的例子
public class IntegrationTest extends AbstractIntegrationTest {
@Test
public void login() {
foo.login("foologin");
bar.login("barlogin");
}
}
我同意@rainerhahnekamp 的观点,他说您想要实现的更像是系统/集成测试。
但是,如果您仍然想以这种方式进行测试,我认为它仍然是可行的。
首先,要知道一件重要的事情:
在test
项目中同时导入fooApp
和barApp
项目将使两个项目的配置文件都可用于类加载器,并且会产生不可预测的结果。 示例:将仅加载两个application.properties
文件之一。 因此,您必须使用 2 个不同的配置文件来加载 2 个单独的配置文件设置。
由于项目文件重叠的相同原因,一个应用程序在另一个应用程序可见的包中定义的 bean 将在两个应用程序上下文中加载。
为了测试这个概念,我在每个项目中创建了一个服务和一个休息控制器,每个都有一个“配置文件”属性文件:
@EnableAutoConfiguration(
exclude = {SecurityAutoConfiguration.class,
ManagementWebSecurityAutoConfiguration.class})
@SpringBootApplication
public class BarApp {
public static void main(String[] args) {
SpringApplication.run(BarApp.class, args);
}
}
@Service
public class BarService {
public String yield() {
return "BarService !";
}
}
@RestController
public class BarResource {
private final BarService barService;
public BarResource(BarService barService) {
this.barService = barService;
}
@GetMapping("/bar")
public String getBar() {
return barService.yield();
}
}
应用-bar.properties:
server.port=8181
@EnableConfigurationProperties
@SpringBootApplication
public class FooApp {
public static void main(String[] args) {
SpringApplication.run(FooApp.class, args);
}
}
@Service
public class FooService {
public String yield() {
return "FooService !";
}
}
@RestController
public class FooResource {
private final FooService fooService;
public FooResource(FooService fooService) {
this.fooService = fooService;
}
@GetMapping("/foo")
public String getFoo() {
return fooService.yield();
}
}
应用程序-foo.properties:
server.port=8282
class TestApps {
@Test
void TestApps() {
// starting and customizing BarApp
{
SpringApplication barApp = new SpringApplication(BarApp.class);
barApp.setAdditionalProfiles("bar"); // to load 'application-bar.properties'
GenericWebApplicationContext barAppContext = (GenericWebApplicationContext) barApp.run();
BarService barServiceMock = Mockito.mock(BarService.class);
Mockito.doReturn("mockified bar !").when(barServiceMock).yield();
barAppContext.removeBeanDefinition("barService");
barAppContext.registerBean("barService", BarService.class, () -> barServiceMock);
}
// starting and customizing FooApp
{
SpringApplication fooApp = new SpringApplication(FooApp.class);
fooApp.setAdditionalProfiles("foo"); // to load 'application-foo.properties'
GenericWebApplicationContext fooAppContext = (GenericWebApplicationContext) fooApp.run();
FooService fooServiceMock = Mockito.mock(FooService.class);
Mockito.doReturn("mockified foo !").when(fooServiceMock).yield();
fooAppContext.removeBeanDefinition("fooService");
fooAppContext.registerBean("fooService", FooService.class, () -> fooServiceMock);
}
RestTemplate restTemplate = new RestTemplate();
String barResourceUrl = "http://localhost:8181/bar";
ResponseEntity<String> barResponse = restTemplate.getForEntity(barResourceUrl, String.class);
String fooResourceUrl = "http://localhost:8282/foo";
ResponseEntity<String> fooResponse = restTemplate.getForEntity(fooResourceUrl, String.class);
System.out.println(barResponse.getBody());
System.out.println(fooResponse.getBody());
}
}
启动测试会产生:
mockified bar !
mockified foo !
顺便说一句,我怀疑您的项目会像我的示例一样简单,并且我怀疑您会遇到与我之前强调的重要事项相关的问题。
您需要配置 @ContextConfiguration 以指向两个应用程序
给定两个包 com.foo.module1 和 com.foo.module2,您必须为每个包创建一个配置类。 例如对于模块 1:
@SpringBootApplication public class Config1 {}
如果您只想使用单个包的 Spring bean 来运行应用程序,您可以使用 SpringApplicationBuilder 来实现。 一个工作片段:
new SpringApplicationBuilder(com.foo.module1.Config1.class)
.showBanner(false)
.run()
这将使用 Config1 启动 Spring,它仅在其包中搜索(@ComponentScan 包含在 @SpringBootApplication 中)bean。
如果您想运行完整的应用程序,例如同时运行所有两个模块,您必须在上层包 com.foo 中创建一个配置类。
在下面提到的情况下,由于 spring-boot-starters 之类的库,在单个应用程序中运行两个模块可能会以不希望的方式相互干扰,我只能想到两种可能性:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.