Testing, a Pervasive Activity

Testing interplays with other stages of the software engineering process. In particular, due to test-first approaches the testing activities are pervasive. Test-first, as a specification activity, intertwines the problem definition with solution coding. Test-driven development, which adds code refactorings to the equation, intertwines coding with software design. Actually, it turns software design into a reverse engineering activity, where patterns are discovered and created from the existing code.

When testing a large system a divide-and-conquer approach should be followed: test the modules in isolation before testing them together, which is called unit testing. This way, when the integration tests occur, the modules are robust enough such that the assessment can focus on the interactions between units. Due to the interactions between modules it is necessary to identify those modules that do not depend on any other to test them first, and then test the modules that use them. This is the bottom-up approach to testing, where the unit test of a module includes the integration testing with the modules it uses.

However, bottom-up integration testing delays the testing of a module until all the modules it uses are implemented and tested. There are two situations where this is a drawback: (1) it delays the testing of the system functionalities, because they require the implementation of all modules; (2) it creates a development dependency constraint between teams developing modules that have dependencies, because unit testing of a module cannot start until the modules it depends on are finished and tested. If a test-first approach to development is followed, situation (2) also hinders the development of a module, not only its testing.

Test doubles is a technique that supports the test in isolation of a module by creating doubles of the modules it uses. These doubles have the minimal behavior required by the module under test. There are several techniques to implement test doubles. Stubs and mocks are the techniques more frequently used. The former is an object that implements the minimal set of functionality necessary for the test cases. It can be seen a prototype which state is used in the test asserts. Mock objects describe the behavior the module under test expects and the test verification is done by comparing the declared expected invocations with the log of actual invocations. The differences between these two types of test doubles are described in detail in Mocks are not Stubs.

The use of test doubles impacts on the development of software because it allows a module to be developed and tested without requiring the development and test of the modules it uses. Before test doubles it was possible to have several teams working simultaneously due to the definition of interfaces between them but they had to delay the testing of the models and could not follow a test-first approach. Test doubles foster the use of testing in any stage of the development process. For instance, Iterative Design can focus the prototype on the aspects being analyzed and delay the implementation of the non-crucial modules, the user interface is implemented while the business logic is mocked.