基于 Spring Boot 的微服务集成测试

Posted

技术标签:

【中文标题】基于 Spring Boot 的微服务集成测试【英文标题】:Integration testing Spring Boot based Microservices 【发布时间】:2015-06-24 15:18:22 【问题描述】:

我已经阅读了许多有关使用 Spring Boot 和 RESTful 服务的指南,其中许多包含有关运行单元测试的信息,其中最著名的是“使用 Spring Boot 构建应用程序”。但是,我还没有看到任何示例说明如何对使用/依赖于其他 Spring Boot 应用程序的 Spring Boot 应用程序进行单元测试,这在云微服务架构中很常见。因此,例如,我们有以下 Spring Boot 服务:

服务中介, 适配器1, 适配器2

ServiceMediator 调用 Adapter1 或 Adapter2,具体取决于输入。

有没有办法在 Spring JUnit 测试中启动和测试 ServiceMediator 之前启动 Spring Boot 服务 Adapter1 和 Adapter2?

【问题讨论】:

您是在谈论单元测试来模拟 serviceMediator 的位置,还是要进行集成测试来确定调用真实服务的位置? 我说的是集成测试。 我想这真的取决于你如何定义它。我只是在寻找一种以自动化方式测试本地机器上所有服务的方法。因此,有没有办法在测试中介之前启动中介所依赖的服务? 我找到了一种方法来做到这一点,但我不知道这是否是最好的方法。我愿意接受更好的方法,所以如果有人有更好的方法,请分享。在测试中介之前,我正在使用 ProcessBuilder 启动适配器。 【参考方案1】:
package controller;


import static org.junit.Assert.assertThat;

import java.io.File;
import java.net.URL;

import mediator.CLPApplication;

import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = CLPApplication.class)
@WebAppConfiguration
@IntegrationTest()
public class ControllerTest 

    @Value("$adapter.dependency.jar.location")
    private String adapterDependencyJarLocation;

    @Value("$adapter.dependency.jar.name")
    private String adapterDependencyJarName;

    @Value("$adapter.url")
    private String adapterURL;

    @Value("$mediator.url")
    private String mediatorURL;

    private URL mediator;
    private URL adapter;
    private RestTemplate template;
    Process process = null;

    @Before
    public void setUp() throws Exception 

        adapter = new URL(adapterURL);
        template = new TestRestTemplate();

        //
        // Start the Atomic adapter
        // 
        System.out.println(adapterDependencyJarLocation);
        System.out.println("Starting Adapter");

        try 
            process = new ProcessBuilder("java", "-jar", adapterDependencyJarName)
                .directory(new File(adapterDependencyJarLocation)).start();

            // Try connecting 5 times with a 5 second pause between each
            // to see if it started. 
            Thread.sleep(5000);
            for(int i = 0; i <= 5; i++) 
                try
                    System.out.println("Testing to see if Adapter is up");
                    template.getForEntity(adapter.toString(), String.class);
                    System.out.println("Adapter Started");
                    break;
                
                catch(RestClientException rce)
                    System.out.println("It's not up yet");
                
                Thread.sleep(5000);
            
         catch (Exception e) 
            e.printStackTrace();
        
    

    @Test
    public void testMediator() throws Exception 
        mediator = new URL(mediatorURL);
        System.out.println("Calling Mediator");
        ResponseEntity<String> response = template.getForEntity(mediator.toString(), String.class);
        System.out.println(response.getBody());
        // Getting back JSON, so check to see if it starts with an open bracket
        assertThat(response.getBody(), Matchers.startsWith(""));
    

    @After
    public void tearDown() 
        if(process != null) 
            process.destroy();
        
    

【讨论】:

【参考方案2】:

process-exec-maven-plugin 可能会有所帮助,因为它允许在 pre-integration-test 阶段(作为标准 Spring Boot 应用程序)启动多个 java 进程,并在集成后测试阶段。

注意:集成测试应该在 integration-test 阶段运行,因为 ma​​ven-failsafe-plugin 应该配置为spring-boot-maven-plugin see。 然后运行我们的集成测试 verify 或更高版本的 maven Lifecycle 应该是目标,因为 integration-test 阶段实际上位于 package 和 验证生命周期see Default Lifecycles。

以下 maven (pom.xml) 配置对我有用:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>1.3.5.RELEASE</version>
            <executions>                    
                <execution>
                    <id>pre-integration-test</id>
                    <goals>
                        <goal>start</goal>
                    </goals>
                    <configuration>
                        <skip>$integration-tests.skip</skip>
                    </configuration>
                </execution>
                <execution>
                    <id>post-integration-test</id>
                    <goals>
                        <goal>stop</goal>
                    </goals>
                    <configuration>
                        <skip>$integration-tests.skip</skip>
                    </configuration>
                </execution>
            </executions>
        </plugin>           

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>2.19.1</version>
            <configuration>
                <skip>$integration-tests.skip</skip>                  
                <includes>
                    <include>**/*IT.java</include>
                </includes>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>integration-test</goal>
                        <goal>verify</goal>
                    </goals>                        
                </execution>                    
            </executions>
        </plugin>

        <plugin>
            <groupId>com.bazaarvoice.maven.plugins</groupId>
            <artifactId>process-exec-maven-plugin</artifactId>
            <version>0.7</version>
            <executions>                    
                <execution>
                    <id>switchboard-process</id>
                    <phase>pre-integration-test</phase>
                    <goals>
                        <goal>start</goal>
                    </goals>
                    <configuration>
                        <name>accounts-service</name>
                        <workingDir>/../../micro-service</workingDir>
                        <waitForInterrupt>false</waitForInterrupt>                          
                        <arguments>
                            <argument>java</argument>
                            <argument>-jar</argument>
                            <argument>$basedir/../micro-service/target/micro-service-$project.version-exec.jar</argument>
                        </arguments>
                    </configuration>
                </execution>
                <!--Stop all processes in reverse order-->
                <execution>
                    <id>stop-all</id>
                    <phase>post-integration-test</phase>
                    <goals>
                        <goal>stop-all</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

test.java 文件夹中有一个集成测试类 (WebServerIT):

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = WebServerApp.class)
@WebIntegrationTest("server.port:0")
public class WebServerIT 

    @Autowired
    private WebApplicationContext webServerAppContext;

    private MockMvc webServerMockMvc;

    @Before
    public void setUp() 
        System.out.println("the test is set up");
        webServerMockMvc = MockMvcBuilders.webAppContextSetup(webServerAppContext).build();
    

    /**
     * This test calls the WebServer's endpoint (/accounts/123456789) which in turn calls the micro-service rest api 
     * which is started using the process-exec-maven-plugin, otherwise the test would fail.
     */
    @Test
    public void testWebServerInteractionWithMicroService() throws Exception 
        this.webServerMockMvc.perform(get("/accounts/123456789"))
                .andExpect(status().isOk());
    

【讨论】:

以上是关于基于 Spring Boot 的微服务集成测试的主要内容,如果未能解决你的问题,请参考以下文章

基于spring-boot的应用程序的单元+集成测试方案

使用 Eureka 服务集成测试 Spring Boot 服务

spring-boot项目的docker集成化部署

Spring Boot JPA 元模型不能为空!尝试运行 JUnit / 集成测试时

Spring Boot教程14——集成测试

使用Spring BOOT集成测试JPA存储库时的NoSuchMethodError(getMetaModel)