preface

This article introduces how to use automatic code generation to quickly and efficiently test microservice apis. It is divided into three parts: requirements, Design, and implementation. The requirements section will cover the project background, pain points in microservice API testing, and our ideal solution. The design chapter will introduce how to design a feasible implementation scheme according to the requirements. The implementation section will start with the code and introduce the implementation. You can click on the part you are interested in.

Generate test code

In order to implement No Code testing, it is inevitable that we need to use Java Code to generate another piece of Java Code. To address this requirement, we adopted JavaPoet.

JavaPoet is an open source Java code generation framework from Square that provides Java Api generation. This framework function is very useful, we can easily use it to generate code according to annotations, database schema, protocol format, etc. By automating code generation in this way, we can replace tedious and repetitive work in a more concise and elegant way.

Test code framework

The basic idea is to define the input required for API testing into a configuration file, and then reference the configuration file in the test code as the parameters of the test. Using JUnit5’s parameterized testing framework makes this idea easy to implement.

Parameterized tests can run tests multiple times with different parameters. They are declared just like the regular @test method, but with the @parameterizedTest annotation instead. In addition, you must declare at least one parameter source that will provide input parameters for each call and then use them in the test method.

The following example demonstrates a parameterized test that uses the @Valuesource annotation to specify a String array as a parameter source.

@ParameterizedTest 
@ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" }) 
void palindromes(String candidate) { 
    assertTrue(StringUtils.isPalindrome(candidate)); 
}
Copy the code

The database

Preset test data

We need to use CSV files or SQL files to save the preset data to the database. A good choice is SpringTest DBUnit, which provides integration between the Spring testing framework and popular DBUnit projects. It allows us to set up and clear database tables using simple annotations, and to check the expected data after the test is complete.

It provides @databasesetup annotations that make it easy to set preset data. It supports SQL, CSV, Xml and other formats.

Check result data

In addition, we need to compare the database snapshot after the test with the expected data in the CSV file to determine whether the test passes. Using the @ExpectedDatabase annotation provided by SpringTest DBUnit, you can compare the database snapshot after the test.

The HTTP access

Processing HTTP requests

We decided to adopt REST Assured to handle HTTP requests and responses. REST Assured is a Java DSL designed to simplify testing REST services built on top of HTTP Builder. It supports POST, GET, PUT, DELETE, OPTIONS, PATCH, and HEAD requests, and can be used to validate the responses to those requests.

Testing and validating REST services in Java is more difficult than using dynamic languages like Ruby and Groovy. REST Assured brings the simplicity of these languages to the Java realm. For example, if your HTTP server in the “http://localhost:8080/lotto/ {id}” returned the following JSON:

 {
    "lotto": {"lottoId":5."winning-numbers": [2.45.34.23.7.5.3]."winners":[
          {
             "winnerId":23."numbers": [2.45.34.23.3.5] {},"winnerId":54."numbers": [52.3.12.11.18.22}]}}Copy the code

You can simply use REST Assured to validate the parts of the response you are interested in:

 @Test public void
 lotto_resource_returns_200_with_expected_id_and_winners(a) {
 
     when().
             get("/lotto/{id}".5).
     then().
             statusCode(200).
             body("lotto.lottoId", equalTo(5),
                  "lotto.winners.winnerId", hasItems(23.54));
 
 }
Copy the code

Checking the HTTP response

For the response data in JSON format, we hope to support format-based verification during the verification, for example, check whether the format of a specific field in JSON is a number or string, ignoring the specific content of the field.

Based on these requirements, we decided to use JsonUnit. JsonUnit is a library for simplifying JSON validation in tests. For example, if we want to check whether field C is a number, we can write:

 // Type placeholders
 assertThatJson("{\"a\":1, \"b\": {\"c\" :3}}")
     .isObject().containsValue(json("{\"c\" :\"${json-unit.any-number}\"}"));
Copy the code

In addition, it supports the following regular expression based validation.

 // Regular expressions
 assertThatJson("{\"test\": \"ABCD\"}")
     .isEqualTo("{\"test\": \"${json-unit.regex}[A-Z]+\"}");
Copy the code

The template

For request and response data, we want to define it in three ways:

  1. Define the data in a template file and configure the path and parameters of the template in Yaml

Based on that, we decided to use Mustache as a template engine. Mustache is a logic-less templating engine that is very simple to use. OpenAPI Generator also uses this technique as a template for code generation. The following is an example template:

 Hello {{name}}
 You have just won {{value}} dollars!
 {{#in_ca}}
 Well, {{taxed_value}} dollars, after taxes.
 {{/in_ca}}
Copy the code

Input parameters are as follows:

 {
   "name": "Chris"."value": 10000."taxed_value": 10000 - (10000 * 0.4),
   "in_ca": true
 }
Copy the code

Output result:

 Hello Chris
 You have just won 10000 dollars!
 Well, 6000.0 dollars, after taxes.
Copy the code

It’s worth noting that Mustache uses {{… }} is used to identify parameters, which does not conflict with the $hu used by JsonUnit.

Executed as a Task in Gradle

We want to be able to generate test code and execute tests using gradle test commands in both local and CI environments.

To implement the above requirements, we need to develop a custom Gradle plug-in. The test code generator I wrote based on Java is called in this plug-in to generate API test code. At the same time, the dependencies of the test task are configured and the custom plug-in is triggered every time the test task is executed.

The function summary

The features of our test code automatic generation tool are summarized as follows:

  • Automatically generate test classes for the API

    • According to the definition of OAS file, JavaPoet is used to automatically generate test classes in unit of Controller Class.
    • Automatically generate test methods in the test Class, based on the Endpoint of the API.
  • Configure test cases

    • Document the parameters of the test method in the test case Specification file (YAML).
    • You can define parameters for multiple test cases in the same test case specification file.
    • Use ParameterizedTest to invoke the test case specification file as parameter input.
  • Prepare test data

    • Use DBUnit to save test data from CSV or SQL files to the database.
    • Set HTTP(path parameters, headers, query parameters, request body) data in a test case specification file (YAML) or in a separate JSON file.
  • Verifying expected data

    • Use DBUnit to validate the expected database data set in the CSV file.
    • Validates the expected data for HTTP(headers, status, response body) set in the test case specification file (YAML) or in a separate JSON file.
  • Perform the test

    • Generate test code and execute tests using custom Gradle plug-ins

conclusion

This article describes how to design our API testing tools using the various OSS available for each of the requirements described above. Subsequent articles will continue to cover the implementation of the tool.

Code word is not easy, if it helps you, please click like attention and share, thank you!

Related articles

  • That’s amazing! Automated microservice API testing: Requirements section
  • That’s amazing! Automated microservice API testing: Design chapter
  • That’s amazing! Automated microservice API testing: Implementation section

Refer to the link

  • JavaPoet
  • Parameterized Tests
  • SpringTest DBUnit
  • REST Assured
  • JsonUnit
  • Mustache
  • Developing Custom Gradle Plugins