Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to conduct unit, integration and system testing of micro-services

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article introduces the knowledge of "how to carry out unit, integration and system testing of micro services". Many people will encounter this dilemma in the operation of actual cases. next, let the editor lead you to learn how to deal with these situations! I hope you can read it carefully and be able to achieve something!

Unit testing of microservices

Unit testing requires that the scope of the test be limited to the service, which ensures the isolation of the test and minimizes the impact of the test. TDD requires programmers to write test cases before actually coding. Of course, at first, all the test cases should fail, and then write code to let the test cases pass one by one. That is, write enough test cases to make the test fail, and write enough code to make the test successful. In this way, the purpose of the programmer's coding will be clearer.

Of course, writing test cases is not the whole story of TDD. After the test is successful, the successful code needs to be refactored in time to eliminate the "bad smell" of the code.

1. Why do you need to ReFactor the Code

The so-called refactoring, in short, is to modify the code without changing the external behavior of the code in order to improve the internal structure of the program.

The premise of refactoring is that the behavior of the code is correct, that is, the functionality of the code has been tested and passed, which is the premise of refactoring. Only the correct code can be refactored.

So, since the code is correct, why take the time to change and ReFactor the code?

The reason for refactoring is that most programmers can't write perfect code. They can't fully trust the code they write, which is why they need to test the code they write, and so does refactoring. To sum up, the following aspects are the reasons why software needs refactoring.

The software may not be right in the first place. There are only a small number of talented programmers, and most of them inevitably make mistakes, so many programmers can't write the right code at once, so they can only constantly test and ReFactor to improve the code. Even a master like MartinFowler admits that his coding level, like most people, requires testing and refactoring.

Over time, the behavior of the software becomes difficult to understand. This phenomenon is especially concentrated in some software with large scale, long history and poor code quality. The implementation of these software, either out of the original design, or chaotic, people can not understand, especially the lack of "living documents" to guide, the code will eventually "rot."

Code that can run is not necessarily good code. Any programmer can write code that a computer can understand. Only by writing code that is easy for human beings to understand is a good programmer.

It is the existence of these facts in the current software industry that promotes refactoring to become one of the indispensable practices in TDD. The programmer reconstructs the program for the following purposes.

Eliminate repetition. When the code is coded for the first time, it is simply to let the program pass the test, during which there may be a lot of duplicate code, as well as the existence of "zombie code", so it is necessary to eliminate duplicate code during the refactoring phase.

Make the code easy to understand and modify. At the beginning, programmers give priority to the correctness of the program and do not pay attention to the specification of the code, so they need to improve the code during the refactoring phase.

Improve the design of software. Good ideas are not done in one fell swoop. When there is a better solution to the previous code, refactoring decisively to improve the software design.

Find Bug to improve quality. Good code not only makes it easy for programmers to understand, but also makes it easy for programmers to find and fix problems. Testing and refactoring complement each other.

Improve coding efficiency and coding level. Refactoring technology helps to eliminate duplicate code, reduce redundant code, and improve the coding level of programmers. The improvement of programmers' coding level will also be reflected in their coding efficiency.

two。 When should refactoring be carried out

So when should programmers do refactoring?

Restructure at any time. In other words, refactoring should be regarded as a habit of development, and refactoring should be as natural as testing.

There are only three things to do, and there are three things to reconstruct. When there is duplication in the code, it is time for refactoring.

When adding new features. New features have been added and the original code structure has been adjusted, which means that unit testing and refactoring are needed.

When correcting an error. After fixing the error, it is also necessary to re-unit test and ReFactor the interface.

Code review. Code review is a good time to discover the "bad smell of code" and, of course, a great opportunity for refactoring.

3. The "bad smell" of code

If a piece of code is unstable or has some potential problems, then the code tends to contain obvious traces, just like the smell that is often emitted before food spoils. These traces are the "bad smell" of the code. The following is a common code "bad smell".

DuplicatedCode: repetition is the root of all evil. The solution is to extract the common function.

LongMethod (too long function): too long function can lead to a series of problems, such as unclear responsibility, difficult to cut, difficult to understand, and so on. The solution is to split the long function into several functions.

LargeClass (too large class): can lead to unclear responsibilities and difficult to understand. The solution is to split it into several categories.

LongParameterList (excessively long parameter column): too long parameter column does not really follow the object-oriented coding method, and it is difficult for programmers to understand. The solution is to encapsulate the parameters into structures or classes.

DivergentChange (divergent change): this class is used when multiple requirements are modified. The solution is to split the code and put together things that always change together.

ShotgunSurgery: it means to change a requirement without encapsulating the change, and then multiple classes will be modified. The solution is to aggregate the modification points and abstract them into a new class.

FeatureEnvy (attachment complex): a class has too many dependencies on other classes, such as a class that uses a large number of members of other classes, which is called FeatureEnvy. The solution is to incorporate the class into the dependent class.

DataClumps (data mud mass): a data mud mass is a mass of data that often appears together. If the data is meaningful, the solution is to turn the structured data into objects.

PrimitiveObsession (basic type paranoia): keen to use basic types such as int, long, String, etc. The solution is to modify it to use classes instead.

SwitchStatements (switch thriller appearance): when there are too many conditions for switch statements to judge, consider using fewer switch statements and using polymorphism instead.

ParallelInheritanceHierarchies (parallel inheritance system): too many parallel classes that are paralleled using class inheritance. The solution is to remove the inheritance relationship from one of the classes.

LazyClass (redundant classes): the solution to these redundant classes is to merge the logic in these no longer important classes into the related classes and delete the old classes.

SpeculativeGenerality (boasting about the future): for these useless classes, just delete them.

TemporaryField (confusing temporary fields): for these fields, the solution is to centralize these temporary variables into a new class to manage.

MessageChains (overcoupled message chain): use the functions and objects you really need instead of relying on the message chain.

MiddleMan (middleman): there is this problem of over-proxy, and the solution is to replace delegates with inheritance.

InappropriateIntimacy: two classes use each other's private range. The solution is to draw a clear line and break it up, or merge it, or change it into a single link.

AlternativeClasseswithDifferentInterfaces (similar classes): these classes are often similar classes, but have different interfaces. The solution is to rename these classes, move functions, or abstract subclass repeating classes to merge into one class.

IncompleteLibraryClass (imperfect library class): the solution is to package a layer of functions or package a new class.

DataClass (naive data classes): these classes are simple and often have only public member variables or simple operation functions. The solution is to encapsulate the related operations and reduce the public member variables.

RefusedBequest (refuse bequest): these classes show that there are many methods in the parent class, but only a limited number of subclasses are used. The solution is to use proxies instead of inheritance relationships.

Comments (too many comments): if there are too many comments, the code is unclear. The solution is to ReFactor before writing comments, get rid of the extra comments, "good code can talk."

4. Reduce the dependence of testing

First of all, we must admit that the dependence between objects is inevitable. Objects and objects complete the function through cooperation, and any object may use the properties, methods and other members of other objects. But at the same time, it is also recognized that the over-complex dependency of objects in code is often discouraged, because the greater the correlation between objects, the greater the scope of impact of a change in the code. this is not conducive to system testing, refactoring and post-maintenance. Therefore, the dependence between codes should be reduced as much as possible in the process of modern software development and testing.

Compared with the traditional JavaEE development model, DI makes the code less dependent on the container and reduces the coupling problem of computer programs. Through simple new operations, the POJO objects that make up the programmer's application can be tested under JUnit or TestNG. Even without Spring or other loC containers, you can use mock to simulate objects for independent testing. Clear layering and componentized code will facilitate the simplification of unit testing. For example, when running unit tests, programmers can replace DAO or resource library interfaces through stub or mock to test service layer objects without having to access persistence layer data. This reduces reliance on infrastructure.

In the process of testing, the real object has uncertain behavior, which may produce unpredictable effects (such as stock market, weather forecast). At the same time, the real object has the following problems.

Real objects are hard to create.

Some behaviors of real objects are difficult to trigger.

Real objects don't actually exist (with other development teams or with new hardware), and so on.

It is precisely because of the problems existing in the testing process of the above real objects that mock testing is widely used in testing.

In the context of unit testing, a mock object is an object that can "simulate" some object interface with some "fictional placeholder" functionality. During testing, these fictional placeholder objects can mimic the expected behavior and results of a component in a simple way, allowing programmers to focus on thorough testing of the component itself without worrying about other dependency issues.

Mock objects are often used for unit testing. To test with a mock object is to create a test method for testing with a virtual object (mock object) that is not easy to construct (such as HttpServletRequest must be constructed in a Servlet container) or more complex objects (such as ResultSet object in JDBC) that are not easy to construct or obtain.

The biggest function of mock is to decouple the coupling of unit tests. If the written code has dependencies on another class or interface, it can simulate those dependencies and verify the dependency behavior invoked.

The key steps for mock object testing are as follows.

Use an interface to describe this object.

Implement this interface in the product code.

Implement this interface in the test code.

In the code under test, the object is referenced only through the interface, so it does not know whether the referenced object is a real object or a mock object.

At present, the main mock testing tools in the Java camp are Mockito, JMock, EasyMock and so on.

The difference between 5.mock and stub

Both mock and stub are intended to replace external dependent objects, and mock is not stub, with the following differences.

The former is called mockist TDD, while the latter is generally called classic TDD.

The former is behavior-based authentication (Behavior Verification), and the latter is state-based authentication (State Verification).

The former uses simulated objects, while the latter uses real objects.

Now let's look at the difference between mock and stub through an example. If the programmer wants to test the behavior of sending mail, he can write a simple stub like this.

/ / Interface to be tested public interface Mailservice () {public void send (Message msg);} / lstub test class public class MailServiceStub implements MailService i private Listmessages = new ArrayList (); public void send (Message msg) {messages.add (msg);} public int numberSent ({return messages.size ();}

You can also use the state verification test method on stub as follows.

Public class orserStateTester {Order order = new Order (TALISKER, 51); MailServiceStub mailer = new MailserviceStub (); order.setMailer (mailer); order.fill (warehouse); / / verify assertEquals (1, mailer.numberSent ();}} by the number of messages sent

Of course, this is a very simple test that only sends a message. Here the programmer has not tested whether it will be sent to the right person or whether the content is correct.

If you use mock, this test looks different.

Lass OrderInteractionTester. .. Public void testorderSendsMail worker fUnFilled () {Order order = new Order (TALISKER, 51); Mock warehouse = mock (Warehouse.class); Mock mailer = mock (MailService.class); order.setMailer ((Mailservice) mailer.proxy ()); order.expects (once ()). Method ("hasInventory"). WithAnyArgument () .will (returnvalue (false)); order.fill (Warehouse) warehouse.proxy ()}

In both cases, stub and mock are used instead of the real MailService object. The difference is that stub uses the method of state confirmation, while mock uses the method of behavior confirmation.

If you want to use state validation in stub, you need to add additional methods to stub to assist with validation. So stub implements MailService but adds additional testing methods.

Integration testing of Micro Services

Integration testing, also known as assembly testing or joint testing, can be said to be a logical extension of unit testing. Its simplest form is to combine two tested units into a single component and test the interface between them. In terms of the basic technologies used, integration testing is similar to unit testing in many ways. Programmers can use the same test runner and build system support. A big difference between integration testing and unit testing is that integration testing uses relatively little mock.

For example, when testing involves a data access layer, the unit test simply simulates the data returned from the back-end database. In integration testing, a real database is used in the testing process. A database is an excellent example of a resource type that needs to be tested and can expose problems.

In the integration testing of micro-service architecture, programmers pay more attention to service testing.

1. Service interface

In the architecture of micro-services, service interfaces are mostly exposed in the form of RESTfulAPI. REST is resource-oriented and uses HTTP protocol to complete related communications. its main data exchange format is JSON, of course, it can also be multimedia types such as XML, HTML, binary files and so on. Operations on resources include getting, creating, modifying, and deleting resources, all of which can be mapped using the GET, POST, PUT, and DELETE methods of the HTTP protocol.

When testing a service, if you only want to test a single service function, then in order to isolate other related services, you need to pile all external service partners. Each downstream partner needs a piling service, then start them during service testing and make sure they are working properly. Programmers also need to configure the services under test to ensure that they can be connected to these piling services during testing. At the same time, in order to imitate the real service, the programmer also needs to configure the piling service to send back the response to the request of the service under test.

The following is an example of a "user vehicle information" test interface implemented in the Spring framework.

Import org.junit.*; import org.junit.runner.*; import org.springframework.beans.factory.annotation.*; import org.springframework.boot.test.autoconfigure.web.servlet.*; import org.springframework.boot.test.mock.mockito.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.BDDMockito.*; import static org.springframework.test.web.servlet.request.MockMvc RequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvc ResultMatchers.* @ RunWith (SpringRunner.class) @ WebMvcTest (UserVehicleController.class) public class MyControllerTests {@ Autowired private MockMvc mvc; @ MockBean private UserVehicleService userVehicleService; @ Test public void testExample (throws Exception {given (this.userVehicleService.getVehicleDetails ("sboot")) .willReturn (new VehicleDetails ("BMW", "X7")); this.mvc.perform (get ("/ sboot/vehicle") .accept (MediaType.TEXT_ PLAIN)) .andexpect (status (). Isok ()) .andExpect (content (). String ("BMW x7");)}}

In this test, the programmer simulates the data VehicleDetails ("BMW", "X7") of the / sboot/vehicle interface with mock, and judges the test results by MockMvc.

two。 Client

There are a large number of clients that can be used to test RESTful services. Can be tested directly through the browser, such as RESTClient, Postman, etc., introduced earlier in this book. Many application frameworks provide class libraries for testing RESTful API, such as RestTemplate of Java platform like RestTemplate of Spring and Client API of Jersey, RestSharp (http:1restsharp.org) of .NET platform and so on. There are also some independently installed REST testing software, such as SoapUI (ttps:/www.soapui.org), of course, the most concise way is to use cURL to test on the command line.

The following is an example of testing whether Elasticsearch starts successfully. You can use cURL directly on the terminal to do the following.

Scurl 'http://localhost:9200/?pretty'

CURL provides a convenient way to submit a request to Elasticsearch, and then you can see a response similar to the one below on the terminal.

"cluster name": "elasticsearch", "cluster uuid": "uqcQAMTtTIO6CanROYgveQ", "version": {"number": "5.5.0", "build_hash": "260387d", "build_date": "2017-06-30T23:16:05.735Z", "build_snapshot": false, "lucene version": "6.6.O"}, "tagline": "You Know, for Search"} system testing of micro services

After the introduction of micro-service architecture, with the increase in the number of micro-services, test cases also increase, and testing work is increasingly dependent on test automation. Build tools such as Maven or Gradle will include testing in their lifecycle, so as long as the relevant unit test cases are written, unit tests and integration tests can be executed automatically during the build process, and you can see the test report immediately after the build is completed.

In the system testing phase, in addition to automated testing, manual testing is still inevitable. Containers such as Docker provide the infrastructure for automation and bring new changes to manual testing.

In the container-based continuous deployment process, the software is eventually packaged into a container image so that it can be deployed to any environment without worrying about the problems caused by inconsistent work variables. Entering the deployment phase means that both integration tests and unit tests have passed.

But this is obviously not the whole story of testing, many of which must be deployed online, such as some non-functional requirements.

At the same time, whether the user's expectations of the requirements are consistent with the original design can not be verified until the product is launched. Therefore, the testing work after launch is still very important.

1. Smoke test

The so-called smoke testing refers to the testing of a newly compiled software version in order to confirm whether the basic functions of the software are normal before formal testing is needed. After the smoke test, the software will carry out the follow-up formal testing. Smoke tests are often performed by version compilers.

Because the smoke test takes a short time and can verify most of the main functions of the software, the smoke test is performed during the daily build of CI/CD.

⒉ blue and green deployment

Blue and green deployments reduce the risk of releasing new versions by deploying new and old versions. The principle is that when a new version (green deployment) is deployed, the old version (blue deployment) still needs to be available in a production environment for some time. If the new version is online and there is no problem with the test, then all production loads will be switched from the old version to the new version.

The following is an example of a blue-green deployment. Where vl represents the old version of the service (blue) and v2 represents the new version (green), as shown in figure 4-2.

Here are a few points for attention.

The blue and green deployment environments are consistent, and the two should be completely isolated (can be different hosts or different containers).

There is a switch-like device between the blue and green environments for switching traffic, such as a load balancer, a reverse proxy or a router.

After the test of the new version (green deployment) fails, you can immediately go back to the old version.

Blue and green deployments are often used in conjunction with smoke testing.

Implement blue and green deployment, the whole process is automated, users will not feel any downtime or service restart.

3.A/B test

Ahand B testing is a new software testing method. The essence of A _ hand B test is to divide the software into two different versions An and B for separation experiment. The purpose of AB testing is to obtain representative experimental conclusions through scientific experimental design, sampling samples, traffic segmentation and small traffic testing, and to ensure that the conclusion is reliable before it is extended to all traffic. For example, after a period of testing, the experimental results show that the B version is highly recognized by users, so the online system can be updated to the B version.

4. Canary release

Canary release is a type of incremental release that is executed by deploying a new version while the original production version of the software is available. In this way, part of the production traffic is diverted to the newly deployed version to verify that the system is performing as expected. These expected contents can be functional requirements or non-functional requirements. For example, programmers can verify that the request response time for a newly deployed service is less than 1 second.

If the new version does not achieve the desired results, it can be quickly traced back to the old version. If the desired results are achieved, more production traffic can be diverted to the new version.

This is the end of the content of "how to do Unit, Integration and system testing of Micro Services". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report