The previous chapter addressed “Why write unit tests?” In this chapter, we will talk about the place of unit testing in the overall testing architecture.

Unit testing is a member of software testing system.

1. Categories of software testing

Software testing mainly falls into the following categories:

  • Unit testing. Test each unit of work of the underlying code. It usually involves a single public method of a class. White-box testing. Fine-grained.

  • Integration testing. Medium-grained testing. Tests collaboration between classes or interactions with external systems to accomplish an internal function. Interactions involving multiple classes. White-box testing. Medium grain size.

  • Functional testing. Coarse-grained testing. Test for each use case (functionality useful to customers or users) of the system. Black box test. Coarse granularity.

  • Performance and load testing. Test system performance and throughput, etc. Black box test.

  • Acceptance testing. Start from the user interface to test and accept the entire system, check whether the system is ready for deployment and production.

Description:

  • White box and black box tests. White-box testing is testing done with internal code visible, including unit tests and integration tests. Written and run by programmers, not testers, to verify that your code and/or interactions are correct. Black box testing is testing done without knowledge/concern about the code and internal structure of the system, including functional testing, performance and load testing, and acceptance testing. Written and run by testers to verify that the functionality and performance of the system meet the needs of customers and users.

  • External function versus internal function. An external function represents a functional requirement (such as deposit, withdrawal, declaration, claim, etc.), defined by the customer or product manager, is where the business value of the system lies, and represents externally visible properties and behaviors of the system. Internal functions, which may be business or technical, are defined by the developer to implement external functions and qualities, and to represent the structure and mechanisms within the system. Often an external function is performed by multiple internal functions in collaboration.

  • Collaborators and external systems. Accomplishing a feature usually involves interaction between multiple classes, or between classes and external systems. An example of interaction with other classes is that when placing an order, the Order service class calls the Pricing service class to get the current price of each item in the order. Interactions with external systems include interactions with databases, caches, messaging middleware, file systems, third-party service providers, and so on. In unit testing, all collaborators and external systems are isolated and simulated. Other tests often work directly with real collaborators and external systems rather than simulations. Integration tests sometimes simulate parts of an external system.

2. Test the pyramid

Mike Cohn in his book “Succeeding With Agile” describes the test pyramid. This metaphor is very graphic, and it lets you know at a glance that testing needs to be layered. It also tells you how many tests you need to write for each layer.

In the figure above, the pyramid from the bottom up is unit testing, service testing (equivalent to functional testing in our category above), and user interface testing (equivalent to acceptance testing in our category above). Test pyramid instructions:

  • The lower the level of testing, the more tests there are; The higher the level of testing, the fewer the number of tests. Unit tests have the largest number of tests. Tests need to be written for each unit of work. In fact, each unit of work often requires multiple tests to be written to ensure comprehensive test coverage for a wide range of possible conditions.

  • The more low-level the test, the more it needs to be able to execute quickly. Upper-level tests allow longer time to complete. All unit tests should be able to complete in milliseconds.

  • The lower the level of testing, the more you need to isolate dependencies. The higher the level of testing, the more real dependencies need to be integrated. Underlying unit testing, isolating and simulating all dependencies (other classes in the system and external systems); The upper level testing needs to integrate other classes, subsystems and external systems in the system for joint testing.

3. Tests are not interchangeable

Tests should not be substituted for each other. For example, unit tests are used to ensure that each unit of work at the lowest level is correct (correctness of parts), and integration tests are used to ensure that multiple classes work together to achieve a higher-order goal (correctness of internal functions) (correctness of assembly). As technicians, we must first make sure that every part works, and then make sure that when assembled, it will perform the desired function.

Unit testing is the most basic test, its importance is indescribable. If the underbase is weak, the superstructure will fall apart. If there are integration tests, there are no unit tests. So when the integration test fails, you can’t be sure whether the parts failed or the assembly failed. Locating and fixing bugs is costly. If there are unit tests already in place to ensure that the parts are correct, when the integration tests fail, we can be sure that the assembly is the problem, not the parts, so that bugs can be located and fixed at a lower cost.

The next chapter covers “Common misconceptions and practices of unit testing”!