Tests in the context of software development have a surprisingly broad field of uses which exceeds “just” testing. Those further uses paint a more colourful picture of tests than just regression keepers.
Tests as a component runtime: a test is the workbench on which the component is developed. Instead of constantly restarting an application server and laboriously clicking myself through to the component I can validate my understanding of the component’s functionality and evolve it much faster in a unit test. The test is an inexpensive, controlled “lab” environment where I can experiment with the new component before connecting it to the rest of the application and potentially breaking something. Using tests as cheap runtime platforms means that they don’t verify the component’s behaviour (yet), but simply wire up mock dependencies, feed it with data and print out results for the developer to see.
Tests document the component’s intended use: by mocking the component’s dependencies, invoking its methods with arguments, verifying interactions with dependencies and returned data, tests document the component’s usage, highlight corner cases and document the author’s intentions. This comes in extremely handy as systems grow and a component’s initial purpose is washed up in a mesh of responsibilities inferred by the component’s interaction with its dependencies.
Tests guard against regressions: this is frequently misunderstood as the sole purpose of testing – although it definitely is a major one! Automated testing guards against regressions and codifies my understanding of contracts applicable to the component’s runtime and dependencies.
Quality assurance and reporting: testing produce a number of useful metrics that show me the direction my project is heading. Test coverage reports highlight frequently taken code paths and discover under-tested or even obsolete code. Weighted bug reports in relation to test coverage can help determine the optimum effort spent on testing (best value-for-money test is still the assembly test). The execution time of tests can hint early in the development cycle at looming performance degradations. In summary, those KPIs provide valuable insight not only into the software being produced, but the production process itself.
Debugging: unit tests and integration tests can pin-point faulty components before they break an application. Finding which component in a complex system is faulty, and why, can be a daunting task which only experienced developers who know the entire application well can carry out – but that one, broken test in a test suite is quickly located and the cause usually doesn’t fall far from it.
Wrapping it up
Tests have a much broader application than just catching regressions: they speed up development, they document assumptions and intentions alike in a useful and reliable manner and provide insight in the product’s quality to all involved stakeholders.