E2E Testing Pipeline for Spring Boot Microservices using OpenFeign Client and Github Actions

E2E Testing Pipeline for Spring Boot Microservices using OpenFeign Client and Github Actions

In this article, I will show you how to quickly setup E2E Testing Pipeline for your Spring Boot Microservices Project using OpenFeign Client and Github Actions.

You can find the full source code here:

  1. E2E Test and Test Helper Classes: https://github.com/greeta-erp/erp-api/tree/master/department-service/src/test/java/net/greeta/erp/services/department
  2. Test Data Controller: https://github.com/greeta-erp/erp-api/blob/master/department-service/src/main/java/net/greeta/erp/services/department/controller/TestDataController.java
  3. Github Actions E2E Pipeline: https://github.com/greeta-erp/erp-api/blob/master/.github/workflows/acceptance-stage.yml
  4. This source code is a part of the GitOps Pipeline on AWS with Spring Boot, Terraform, Kubernetes and Github Actions. See the full description and links to the source code here: https://www.garudax.id/posts/michaelsklyar_cloud-native-gitops-microservices-minimum-activity-7096286674795520000-SgQe?utm_source=share&utm_medium=member_desktop


  1. TestDataController - This Controller has REST API Endpoints, which delete test data. You can add any other test utility endpoints here. Please, make sure that this Controller is only deployed to DEV and Staging environments. It should never be deployed to Production! Use Spring Boot or Maven Profiles to enable such behaviour. (I decided to skip this part for simplicity)

@RestController
@RequestMapping("/test-data")
public class TestDataController {

    private static final Logger LOGGER = LoggerFactory.getLogger(DepartmentController.class);

    DepartmentRepository repository;

    public TestDataController(DepartmentRepository repository) {
        this.repository = repository;
    }

    @DeleteMapping("/delete/department")
    public ResponseEntity<Void> deleteAllDepartments() {
        repository.deleteAll();
        return ResponseEntity.noContent().build();
    }

    @DeleteMapping("/delete/department/{id}")
    public ResponseEntity<Void> deleteDepartment(@PathVariable("id") String id) {
        repository.deleteById(id);
        return ResponseEntity.noContent().build();
    }

}        

https://github.com/greeta-erp/erp-api/blob/master/department-service/src/main/java/net/greeta/erp/services/department/controller/TestDataController.java

2. application-test.yml - In this configuration file, you provide actual base URLs to all your REST API Clients. These URLs are used by Feign Client. You also need to disable SSL validation, if you are using SSL in your testing environment.

spring.cloud.openfeign.client.config.department.url=https://erp.greeta.net/department
spring.cloud.openfeign.client.config.testData.url=https://erp.greeta.net/department/test-data
feign.httpclient.disableSslValidation=true        

https://github.com/greeta-erp/erp-api/blob/master/department-service/src/test/resources/application-test.yml

3. FeignClientConfiguration - This Feign Client Configuration disables SSL validation for your clients. You need this configuration for testing purposes, if you are using SSL in your testing environment.

@Configuration
public class FeignClientConfiguration {

    @Bean
    public Client feignClient()
    {
        Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
        return trustSSLSockets;
    }

    private SSLSocketFactory getSSLSocketFactory() {
        try {
            TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            };

            SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
            return sslContext.getSocketFactory();
        } catch (Exception exception) {
        }
        return null;
    }
}        

https://github.com/greeta-erp/erp-api/blob/master/department-service/src/test/java/net/greeta/erp/services/department/config/FeignClientConfiguration.java

4.TestDataClient - This is the client for your Test Data REST API Controller, which you created in Step 1. OpenFeign is similar to Spring Data JPA, but for REST API. As you see, in this and the next example, you provide annotated interface with strong typed method parameters and OpenFeign automatically implements this Interface. You can then use such clients in the main code and in the tests and it will feel as if you are working with simple Java objects, not actual HTTP Client behind the scene.

@FeignClient(name = "testData")
public interface TestDataClient {

    @DeleteMapping("/delete/department")
    public void deleteAllDepartments();
}        

https://github.com/greeta-erp/erp-api/blob/master/department-service/src/test/java/net/greeta/erp/services/department/client/TestDataClient.java

5. DepartmentTestClient - This is the client for Department REST API Controller (You can easily find DepartmentController in the Github Source code). OpenFeign is similar to Spring Data JPA, but for REST API. You provide annotated interface with strong typed method parameters and OpenFeign automatically implements this Interface.

@FeignClient(name = "department")
public interface DepartmentTestClient {

    @GetMapping("/{id}")
    public Department findById(@PathVariable("id") String id);

    @PostMapping("/")
    public Department add(@RequestBody Department department);

    @GetMapping("/")
    public Iterable<Department> findAll();

    @GetMapping("/organization/{organizationId}")
    public List<Department> findByOrganization(@PathVariable("organizationId") String organizationId);
}        

https://github.com/greeta-erp/erp-api/blob/master/department-service/src/test/java/net/greeta/erp/services/department/client/DepartmentTestClient.java

6. DepartmentE2ETest - This is the actual E2E Test, which uses OpenFeign Clients for REST API Calls. In these tests you can use any Java classes from your main code. Any domain objects, which you use in the main code or in other tests can be reused in your E2E tests. In contrast to RestAssured, you have real strong typing here: no need to learn complex logic for resolving JSON structure. In contrast to SwaggerCodeGen, you don't need to generate any Source Code and regenerate it after any changes in REST API Controllers. OpenFeign Clients are completely decoupled from REST API Controllers in your main code. You can change them independently. Also, if anything changes in REST API Controllers and your OpenFeign Clients are not changed, then your E2E tests will fail, which makes your tests very practical regression tests, which are in sync with your REST API.

@SpringBootTest
@TestPropertySource(locations = {
        "classpath:application.yml",
        "classpath:application-test.yml"
})
public class DepartmentE2eTest {

    @Autowired
    private DepartmentTestClient departmentClient;

    @Autowired
    private TestDataClient testDataClient;

    @BeforeEach
    void cleanup() {
        testDataClient.deleteAllDepartments();
    }

    @Test
    void addDepartmentTest() {
        Department department = new Department("1", "Test");
        department = departmentClient.add(department);
        assertNotNull(department);
        assertNotNull(department.getId());
    }

    @Test
    void addAndThenFindDepartmentByIdTest() {
        Department department = new Department("2", "Test2");
        department = departmentClient.add(department);
        assertNotNull(department);
        assertNotNull(department.getId());
        department = departmentClient.findById(department.getId());
        assertNotNull(department);
        assertNotNull(department.getId());
    }

    @Test
    void findAllDepartmentsTest() {
        Department[] departments = StreamSupport.stream(departmentClient.findAll().spliterator(), false).toArray(Department[]::new);
        assertEquals(0, departments.length);
    }

    @Test
    void findDepartmentsByOrganizationTest() {
        Department department = new Department("1", "Test");
        department = departmentClient.add(department);
        assertNotNull(department);
        assertNotNull(department.getId());
        List<Department> departments = departmentClient.findByOrganization("1");
        assertEquals(1, departments.size());
    }

}        

https://github.com/greeta-erp/erp-api/blob/master/department-service/src/test/java/net/greeta/erp/services/department/DepartmentE2eTest.java

7. pom.xml - This change is required, in order to prevent other tests from running E2E tests. E2E tests should be run in separate pipeline, when DEV or Staging Environments are ready. It is not recommended to run such tests in your standard testing pipeline, because they depend on your DEV or Staging environment and usually take longer time to run, than other tests.

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>**/*E2eTest.java</exclude>
                    </excludes>
                    <failIfNoSpecifiedTests>false</failIfNoSpecifiedTests>
                </configuration>
            </plugin>        

https://github.com/greeta-erp/erp-api/blob/master/pom.xml

8.acceptance-stage.yml - This is example of Github Actions workflow file, which triggers E2E Testing Pipeline. The best way is to integrate this workflow with your deployment to DEV or Staging. In this simple example, you can run this workflow manually, after you deploy your Envrionment to AWS with Terraform (or use your own preferred way of infrastructure deployment). See this post for more details on Deployment to AWS with Terraform: https://www.garudax.id/feed/update/urn:li:activity:7096286674795520000/

name: Acceptance Stage
on:
  workflow_dispatch:

jobs:
  e2e:
    name: E2E Tests
    runs-on: ubuntu-22.04
    permissions:
      contents: read
      packages: write
      security-events: write
    steps:
      - name: Checkout source code
        uses: actions/checkout@v3
      - name: Set up JDK
        uses: actions/setup-java@v3
        with:
          distribution: temurin
          java-version: 17
          cache: maven
      - name: Run E2E Tests
        run: |
          mvn test -Dtest="*E2eTest"        

https://github.com/greeta-erp/erp-api/blob/master/.github/workflows/acceptance-stage.yml

Congratulations! You were able to quickly set up E2E Testing pipeline in your environment. Now you are ready to make more tests and add more test coverage to your REST API.

Article content

It is recommended to add new E2E Test after every Exploratory Manual Testing Session. If you follow this practice, you will have strong testing coverage with great executable documentation in the form of E2E tests. As a next step, I would recommend to use Gherkin Testing Scenarios and translate them to E2E tests. This will make your E2E tests real executable documentation, completely in sync with your main code. Confluence or Wiki Documentation is great! But it quickly becomes outdated. With your E2E tests it will never happen. Your documentation is always with you, ready to prove that its up-to-date any time.

How can I write FeignClient method for the Spring controller returning ResponseEntity<StreamingResponseBody> This endpoint is being used to generate Excel File

Like
Reply

Do we need to set up maven also in the workflow, as I am getting errors in the pipeline? mvn: command not found

Like
Reply
Chun-Wei Chang

Software Engineer | MSCS @ SCU | PKU alumni

2y

Hi, I am confused about your E2E test. I thought the E2E test would have multiple services communicate with each other. That said, the Department E2E test should communicate with other services such as Organization, Employee, etc. So, how do you spin up other services while running an E2E test? I think that's more important. I don't see you do something similar to this in your E2E test, did I miss something?

Like
Reply

To view or add a comment, sign in

More articles by Mykhailo Skliar

Others also viewed

Explore content categories