How to conduct integration testing of microservices

The concept of integration testing

When it comes to integration testing, I believe that every test engineer is familiar with it. It is not a new concept. You can know its meaning in traditional software testing through the Wikipedia definition.

Integration testing (sometimes called integration and testing, abbreviated I&T) is the phase in software testing in which individual software modules are combined and tested as a group. Integration testing is conducted to evaluate the compliance of a system or component with specified functional requirements.

That is, integration testing (sometimes called integration and testing, or I&T for short) is the phase in software testing in which individually developed software modules are combined and tested as a whole in order to assess whether a system or component conforms to specified functional requirements.

Integration tests are also required under the microservice architecture, and related tests need to be carried out for the communication between different methods of different services. Because when unit testing microservices, unit test cases only verify the internal logic of the unit under test, and do not verify the modules it depends on. Even if the unit tests for service A and service B pass separately, it does not mean that the interaction between service A and service B is normal.

For microservice architectures, integration testing typically focuses on validating subsystems or modules that communicate with external components such as data stores or other microservices. The goal is to verify that these subsystems or modules can properly communicate with external components, not to test that external components are working properly. Therefore, the integration test under the microservice architecture should verify the basic communication paths between the subsystems to be integrated and external components, including correct paths and wrong paths.

Integration testing under microservice architecture

As shown in the figure above, the gateway component layer (Gateways+Http Client+External Service) contains the logic for accessing external services, usually including an HTTP/S client, which will connect to another microservice or external service in the system. The data persistence layer (Date Mappers/ORM) is used to connect to external data stores.

That is, the integration test under the microservice architecture mainly includes two parts:

Gateway component layer, the communication path between microservice components and external services;

Data persistence layer, the interaction between the database access module and the external database.

Please note here that because it is necessary to test whether the communication between subsystems under the microservice and the communication of external services are correct, ideally, you should not use test doubles (Test Double) for external components.

Let's take a look at how these two parts are integrated and tested one by one:

(1) Gateway component layer integration test

Suppose there is a login service that needs to know the current time, and the time is provided by an external time service. When a GET request is made to /api/json/cet/now, the status code is 200 and the complete time information is returned as follows.

{ 
$id: "1", 
currentDateTime: "2020-07-29T02:11+02:00", 
utcOffset: "02:00:00", 
isDayLightSavingsTime: true, 
dayOfTheWeek: "Wednesday", 
timeZoneName: "Central Europe Standard Time", 
currentFileTime: 132404622740972830, 
ordinalDate: "2020-211", 
serviceResponse: null, 
}

 If the accessed URL is wrong, for example, when sending a GET request to /api/json111/cet/now, the status code is 404, and the following error message will be returned.

In general, integration tests are responsible for verifying connections to external services and issues related to the interaction protocol, such as missing HTTP headers, exceptions in SSL handling, or request/response mismatches. All error handling logic needs to be covered in tests to ensure that the used services and protocol clients respond as expected in special cases.

(2) Data persistence layer integration test

The integration test of the data persistence layer is more complicated, because the results will be stored on the storage system and persisted, and the execution of each test may affect the execution of subsequent tests due to data changes. This means that the two tests are not completely independent since they operate on common data.

In most cases, it should be ensured that the external factors between the two tests are also independent of each other. Because such errors (test execution failures caused by the modification of test data) are often difficult to realize after they occur, which in turn affects the progress of the investigation.

In order to ensure the independence of the two tests, the common steps of the persistence layer integration test are:

  1. Before performing any tests, roll back the database to a known and predictable state, which requires cleaning/rolling back previous modifications to the database;

  2. Rebuild the database by inserting data known and expected for the test;

  3. carry out relevant tests;

  4. Repeat the above process.

Common Problems and Solutions

However, there are many times when external services are unavailable (the service has not been developed yet, or the service has block-level defects that have not been fixed), or its abnormal behavior (such as timeout of external components, slow response, etc.) is difficult to verify. External components cannot use test doubles, external services are unavailable, or abnormal scenarios are difficult to construct. It seems that there is no solution, but in fact there are alternatives.

service is not available

For situations where services are unavailable, microservice virtualization technology can perfectly solve this problem. It is a necessary tool to avoid surprises when communicating with other services, especially when working in an enterprise environment with a large number of dependencies. It can be used during the testing phase to remove dependencies on third-party services, to test how the application behaves when it encounters latency or other network issues. It realizes the simulation of dependent services by creating proxy services, which is especially suitable for testing communication between services. Common tools include Wiremock, Hoverfly, Mountebank, etc.

Taking Wiremock as an example, the effect of the following code is: when the relative URL completely matches /api/json/cet/now, it will return a status of 200, and the response body is similar to the return value of /api/json/cet/now, Content- The value of Type Header is text/plain. Otherwise, when the relative URL is wrong, such as accessing /api/json111/cet/now, a 404 error will be returned.

@Test 
public void exactUrlOnly() { 
    stubFor(get(urlEqualTo("/api/json/cet/now")) 
            .willReturn(aResponse() 
                .withHeader("Content-Type", "text/plain") 
                .withBody(equalToJson("{ 
                      $id: \"1\", 
                      currentDateTime: \"2020-07-29T02:11+02:00\", 
                      utcOffset: \"02:00:00\", 
                      isDayLightSavingsTime: true, 
                      dayOfTheWeek: \"Wednesday\", 
                      timeZoneName: \"Central Europe Standard Time\", 
                      currentFileTime: 132404622740972830, 
                      ordinalDate: \"2020-211\", 
                      serviceResponse: null, 
                      }")))); 
    assertThat(testClient.get("/api/json/cet/now").statusCode(), is(200)); 
    assertThat(testClient.get("/api/json111/cet/now").statusCode(), is(404)); 
}

Service timeout & slow response difficult to construct

If you use a real service test, it is more convenient to use various tools, such as Fiddler, Dummynet, Clumsy, etc., when the service timeout or slow response needs special construction.

Wiremock also supports delay functions, such as using withFixedDelay() to achieve a fixed delay effect:

stubFor(get(urlEqualTo("/api/json/cet/now")).willReturn( 
        aResponse() 
                .withStatus(200) 
                .withFixedDelay(2000)));

Use withLogNormalRandomDelay() to achieve a random delay effect:

stubFor(get(urlEqualTo("/api/json/cet/now")).willReturn( 
        aResponse() 
                .withStatus(200) 
                .withLogNormalRandomDelay(90, 0.1)));

Data initialization and construction are expensive

Although the above-mentioned methods for integration testing of the data persistence layer are general, it is necessary to write a large number of sample codes to initialize the database, and it is also necessary to write a large number of database operation statements to insert expected data. Faced with this problem, some ready-made persistence testing frameworks can be used to improve the testing experience. Common persistence testing frameworks include NoSQLUnit, DBUnit, etc.

The design concept of DBUnit is to back up the database before the test, and then implant the data that needs to be prepared into the object database. After the test is completed, read the backup database and initialize it to the state before the test. DBUnit can compare the operation results of the database during the life cycle of the test case. The databases supported by DBUnit include db2, h2, mssql, mysql, oralce, postgresql, etc.

NoSQLUnit is a DBUnit-like way to write tests for NoSQL databases. Supports a variety of NoSQL databases, including HBase, MongoDB, Redis, ElasticSearch, Vault, Neo4j, etc.

Summarize

This lesson explains the definition of integration testing under the microservice architecture, and then explains two aspects of integration testing under the microservices: gateway component layer integration testing and data persistence layer integration testing.

In the integration test of the gateway component layer, the simulation of external service capabilities is realized through service virtualization technology, and the situation of external service timeout and slow response is constructed by simulating network abnormalities.

In the data persistence layer integration test, the persistence test framework can avoid writing a large amount of code and a large number of SQL statements during the conventional persistence test.

Of course, the power of the above frameworks and tools is not limited to this. Only key example information is given in this article, and you can explore and learn by yourself according to your needs or interests.

Finally, I would like to thank everyone who has read my article carefully. Reciprocity is always necessary. Although it is not a very valuable thing, you can take it away if you need it:

insert image description here

Software testing interview applet

The software test question bank maxed out by millions of people! ! ! Who is who knows! ! ! The most comprehensive quiz mini program on the whole network, you can use your mobile phone to do the quizzes, on the subway or on the bus, roll it up!

The following interview question sections are covered:

1. Basic theory of software testing, 2. web, app, interface function testing, 3. network, 4. database, 5. linux

6. web, app, interface automation, 7. performance testing, 8. programming basics, 9. hr interview questions, 10. open test questions, 11. security testing, 12. computer basics

These materials should be the most comprehensive and complete preparation warehouse for [software testing] friends. This warehouse has also accompanied tens of thousands of test engineers through the most difficult journey. I hope it can help you too!   

Guess you like

Origin blog.csdn.net/qq_48811377/article/details/132496393