How to write effective interface tests?

In all development tests, interface testing is an essential item. Effective and complete interface testing can not only guarantee the development quality of new functions, but also allow the development to have the ability to return when modifying the function logic, and it is also a prerequisite for elegant refactoring. What principles should be followed when writing interface tests? What should the structure of the test code look like? What are some practical tips for interface testing? This article shares the author's practical summary on interface testing.

First-line development students may have more or less caused online bugs or even failures; also encountered such a scenario, a student refactored the code when developing a certain function, causing online bugs or failures; When it comes to a certain function, it is found that the public logic needs to be modified, fearing that other functions will be affected, so the code is very unsightly copied, and a separate logic is rewritten to support it.

The above situations all contain a key issue, whether it is function development or logic refactoring, how to ensure the quality of code development. The means of assurance, everyone knows, is testing. The first is new function testing to ensure the logic of new functions is correct; the second is regression testing to ensure the logic of original business functions is correct. There are generally two ways of testing, manual testing and automated testing. With the continuous development of testing technology and tools, the proportion of manual testing is gradually reduced and gradually replaced by automated testing. Automated testing is sustainable and repeatable, and even AI-enabled.

a test layer

Tests are also layered, as shown in the following diagram:

In a system, automated testing is generally divided into unit testing, module testing and interface testing.

unit test

At present, my application code is basically based on the interface-oriented programming model of the spring framework, and the unit test has been weakened. The requirements of unit testing are basically the testing of a single method of a single class. In our current mode, the cost of writing is too high. Of course, if it is a tool or a relatively cohesive and complex logic (such as algorithm logic), unit testing should still be used to ensure the correctness of the logic.

module test

In the case of a relatively large system with many modules, a module test layer can be established to ensure the correctness of the functions of each module. However, the current system development trend is the microservice architecture, so the module test layer is not very necessary, and can be covered by the interface test layer.

interface test

Personally, I think it should be called entrance test to be precise. This layer is an integration test starting from the system entrance. The application entry is usually HSF (a distributed RPC service framework) service, message, and timed task.

As a development, there are tens of thousands of testing methods, and interface testing is indispensable. When the interface test of our application is effective and the coverage is complete, it can not only guarantee the development quality of our new functions, but also allow us to have the ability to return when modifying the function logic. At the same time, this is also the premise of our code refactoring. At the same time, testability is also an indicator of a reasonable code structure. If it is found that a piece of code is difficult to write a test script or cannot be tested, it means that the current code structure is unreasonable and needs to be refactored. Next, I will mainly talk about the effectiveness of interface testing.

Two testing principles

Basic principles:

  • Automation: Interface testing is a non-interactive automated execution that does not require human participation.

  • Independence: Interface tests should not depend on each other.

  • Repeatable: Interface testing can be performed repeatedly, regardless of the environment.

  • Interface testing complies with BCDE principles to ensure the quality of interface delivery.

  • Border: Boundary test.

  • Correct: correct input, correct expected output.

  • Design: Write test logic according to requirements and design documents.

  • Error: Bad input, expected output.

  • Data preparation: Data preparation is performed through system services, not through direct insertion into db.

  • Testability: Untestable code needs to be restructured into a reasonable structure.

  • Coverage: Interface testing needs to cover all UCs. At the same time, code coverage and branch coverage should reach certain standards, and new code must be covered.

  • Continuity: If code modification causes existing interface tests to fail, you must fix the code problem or test the code logic.

  • Time requirement: Interface testing should be completed before the project is released, and should not be added after the project is released.

The above basic principles should be applicable to automated test cases of all layers. When writing interface tests, in addition to the above principles, there are other principles that need to be followed. Let's look at a picture first:

To analyze the entry call from the perspective of the system, take the HSF service as an example:

  • Peripheral systems call services provided by our system.

  • The system executes a bunch of code logic, which includes branch logic.

  • During the execution of the system, it relies on the external HSF service, calls it, and gets the return value.

  • During the execution of the system, it relies on DB query or landing data, and relies on cache query or landing data.

  • A message is sent externally during system execution.

  • Return the HSF execution result to the upstream system.

The key principle of effective interface testing is to cover all entries, mock all dependencies, and verify the traces left during the execution process. The summary is as follows:

  • Entry coverage: Interface test cases must cover HSF service entry, message entry, and scheduled task entry.

  • Rely on mock: Among the basic principles, there is the principle of repeatability, that is, interface testing cannot be dependent on the environment, and external dependencies need to be mocked. However, for db dependencies, it is not recommended to completely mock them. On the one hand, the cost of mocking is high, and on the other hand, SQL and table constraint logic may not be covered.

  • Complete verification: An effective interface test should have complete verification, and an interface test without verification is meaningless. As long as the traces left during the execution have an impact on the business, a complete verification must be performed to ensure the effectiveness of the interface test.

  • HSF interface return value verification: Perform HSF return parameter verification according to the scenario and interface agreement.

  • DB verification: verify the correctness of landing data.

  • Cache verification: verify the correctness of the data stored in the cache.

  • HSF dependent input parameter verification: Obtain the input parameters dependent on HSF calls through the mock tool, and perform input parameter verification.

  • Message verification: Obtain the sent message object through the mock tool, and perform message body verification.

Three test code structure

When writing test code, you should also consider the readability, scalability, and reusability of the code just like writing business code. At the same time, according to the business characteristics of the system, test components suitable for the current system can be packaged on the basis of the test framework to improve the efficiency of test code writing and standardize the test code structure.

The approximate structure of the test code for an interface is as follows:

1 Test preparation

Depends on data preparation

Many times, our tests have data dependencies, which may be configuration data or business data (for example, refunds need to rely on payment data).

  • Configuration data: Configuration can be initialized by defining a configuration file.

  • Business data: This type of data should not be generated by directly inserting data, but should be generated by calling business services.

rely on mock

For external dependencies, it is necessary to mock the dependent services to avoid real calls.

Interface test entry preparation

Prepare the input parameters for the interface.

2 Test Execution

Call the interface method to execute business logic.

3 Test verification

  • Return parameter verification: verify the return parameters of the interface.

  • DB: Verify the landing data of DB.

  • Cache data verification: verify the data landed in the cache.

  • Message verification: verify the message object sent externally.

  • External HSF call verification: verify the input parameters of external HSF calls.

Four Practical Tips

1 Execution efficiency

For interface testing, execution efficiency is a point that has to be paid attention to. If an interface test is executed for more than 3 minutes to see the results, it will greatly reduce the enthusiasm of developers to write interface tests. For the improvement of test execution efficiency, the suggested solution is:

  • Minimize the startup test context, such as the application of spring boot, just start spring

  • Use an in-memory database such as h2

  • Mock out middleware dependencies

2 Test framework selection

For the test framework, it is recommended to choose a test framework based on testng, which can provide data preparation through configuration files. If you can't find a suitable one, you can package it yourself based on testng.

3 Interface test coverage

The integrity of the scenario affects the coverage of the test cases. On the one hand, developers need to enumerate normal and abnormal situations based on the input and test experience of the business scenario. On the other hand, the interface method also has some fixed points that need to be tested, such as idempotent testing , boundary value tests, parameter incorrect tests, etc.

At the same time, use the coverage tool to view the code or branch logic not covered by the interface, and perform targeted scenario coverage testing. In my experience, full branch coverage is very important, especially unusual branches.

Five Summary

To ensure the stable online operation of the system, quality assurance measures are essential. Although there are many automated means of assurance, interface testing is still one of the most basic and important means of assurance. If we can continuously ensure the coverage and effectiveness of interface tests, the occurrence of online bugs will be greatly reduced, and developers will be more motivated to refactor the code.

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:

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, and I hope it can help you! Partners can click the small card below to receive 

Guess you like

Origin blog.csdn.net/kk_lzvvkpj/article/details/131194588