Spring Boot-如何从多模块项目中的姐妹模块启动Spring Boot Application?

时间:2018-12-09 13:07:55

标签: java spring spring-boot testing integration-testing

我有一个包含两个项目的多模块项目:后端客户端。后端是普通的Spring Boot Rest API,没什么特别的。 client 模块只是使用Rest API的Java库。

后端也使用JSP封装了“ war”作为后端,因此也需要部署到servlet容器中。使用@SpringBootTest仍然可以轻松测试后端。

现在,我想在 client 模块中使用后端模块作为沙箱服务器进行一些集成测试。

要在我添加的客户端模块中使用所有后端类

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-war-plugin</artifactId>
  <configuration>
    <attachClasses>true</attachClasses>
  </configuration>
</plugin>

,并将后端配置为带有类的客户端中的测试依赖项

在我的client / src / test / java中,我有一个帮助程序类,用于启动后端模块

@Configuration  
public class SandboxServer {

  @Bean
  public ConfigurableApplicationContext backend() {
    return 
      new SpringApplicationBuilder(BackendApplication.class)
      .sources(SandboxServerConfig.class)
      .run("spring.profiles.active=sandbox")
  }
}

配置文件“沙盒”用于设置测试数据库等。但是我遇到了更多问题。第一个问题是关于文档根目录,因此我对其进行了配置:

public class SandboxServerConfig 
  implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
  @Override
  public void customize(TomcatServletWebServerFactory factory) {
    factory.setDocumentRoot(new File("../backend/src/main/webapp"));
  }
}

但是它仍然不起作用,因为Spring不会选择backend / src / main / resources / application.properties

这可能是正确的,因为它不在客户端模块的根类路径中。

所以它实际上不起作用。我想不可能只在集成测试中启动同级模块。

如何实现启动同级spring boot模块进行集成测试?这样的szenario的最佳实践是什么?

2 个答案:

答案 0 :(得分:1)

您可以像这样使用TestPropertySource覆盖application.properties位置:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = BlaApplication.class)
@TestPropertySource(locations="/path/to/backend/src/main/resources/application.properties")
public class ExampleApplicationTests {

}

答案 1 :(得分:0)

我找到了更可靠的解决方案。在我的兄弟项目“前端”中,我有一个组件,当且仅当该服务器尚未运行时,该组件才以集成模式启动后端服务器。

好处:

  • 真正的战争已经过测试
  • 您可以先在IDE中启动WAR,然后让测试快速运行
  • 如果您使用maven运行它,它将在所有测试之前启动一次
  • 不需要构建配置(例如在Maven中进行预集成)
  • 进程与Junit运行时是分开的,因此不必担心复杂的设置。

缺点:

  • 您需要先构建软件包,然后才能在前端中运行任何集成测试。但是,嘿,您应该在测试之前构建您的软件包。这就是集成测试的全部内容。

这是我的SandboxServerProcess.class。

import org.springframework.stereotype.Component;   
import javax.annotation.*;
import javax.annotation.*;
import java.io.*;
import java.net.*;
import java.util.*;

@Component
@Profile("integration")
public class SandboxServerProcess {
    private static final String WAR = "../backend/target/backend.war";
    private final static int PORT = 8081;
    private boolean startedByMe;

    @PostConstruct
    public void start() throws Exception {
        if (isStarted()) {
            return;
        }
        testWarExists();
        packagedWar("start");
        if (waitForStartup()) {
            startedByMe = true;
            return;
        }
        throw new RuntimeException("Sandbox Server not started");
    }

    private void testWarExists() {
        File file = new File(WAR);
        if (!file.exists()) {
            throw new RuntimeException("WAR does not exist:" + file.getAbsolutePath());
        }
    }

    @PreDestroy
    public void stop() throws IOException {
        if (startedByMe) {
            packagedWar("stop");
        }
    }

    private void packagedWar(String command) throws IOException {
        ProcessBuilder builder = new ProcessBuilder();
        builder.environment().put("MODE", "service");
        builder.environment().put("SPRING_PROFILES_ACTIVE", "integration");
        builder.environment().put("APP_NAME", "backend");
        builder.environment().put("PID_FOLDER", "./");
        builder.environment().put("LOG_FOLDER", "./");
        List<String> commands = new ArrayList<>();
        commands.add(WAR);
        commands.add(command);
        builder.command(commands);
        builder.inheritIO();
        builder.redirectErrorStream(true);
        builder.start();
    }

    private boolean isStarted() {
        try {
            Socket socket = new Socket();
            InetSocketAddress sa = new InetSocketAddress("localhost", PORT);
            socket.connect(sa, 500);
            logger.warn("SandboxServer is started");
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    private boolean waitForStartup() throws InterruptedException {
        for (int i = 1; i < 30; i++) {
            if (isStarted()) {
                return true;
            }
            logger.warn("SandboxServer not yet ready, tries: " + i);
            Thread.sleep(1000);
        }
        return false;
    }
}