Unit testing is now rightly accepted as an integral step of software development. Software reliability feeds directly into business sustainability for many companies: broken software can tarnish a business’ reputation, cause major disruption for customers, and cost a business a lot of money.
Particularly as component-based software has become standard, the impact of broken code can be widespread and not limited to only one feature. A high degree of test coverage for an application is very important. One approach to unit testing is Test-Driven Development.
The basic idea behind Test-Driven Development is that unit tests are written before a piece of code is written. Whilst being a simple concept in and of itself, its benefits are manifold.
Setting The Goal Posts At The Beginning
Something that can frequently occur when writing unit tests for code is that the developer (either consciously or subconsciously) writes tests that suit the code that has been written, i.e. the goal posts are positioned after the player has kicked the ball. All too often, in order to complete a task, tests are written which the developer knows will pass and those that will not be passed are conveniently forgotten about.
By writing the tests at the outset, the developer has stated the objectives before writing any code and these objectives must then be met, as opposed to using the outcome of the code to determine what the objectives are.
Devising a complete range of assertions for a piece of code at the outset enables a developer to define the goals that the code must achieve. The objective is set and thereafter, the job is to map out the most efficient path to achieving those goals. These assertions should not just mark out the main objectives of the code, but they should also determine how various scenarios are to be handled by the code. In this way, the engineer is forced to consider all use cases that may arise before any code is written.
Without getting into the details of any of the tests, the assertions ensure that there is a clear focus for the developer on the code’s raison d’être and help to prevent drift from occurring in the code.
Assessing Overall Structures
One characteristic of writing software is that sometimes, it can be hard to see the wood from the trees. Regularly taking a step back and assessing the bigger picture is crucial to writing clean and maintainable code. Often, it can be all too easy to get lost in the complexities of a particular function and fail to clearly keep track of how all of the code connects together efficiently.
With the approach of Test-Driven Development, writing unit tests at the outset facilitates clear thinking about the fundamental flow and structure of the code. Basic pieces of functionality are designed and constructed without getting into lower level details.
Identify Helper And Utility Functions Early On
By assessing the overall code structures early on, it is easier for the developer to identify any helper or utility functions that may be required in order to achieve the objectives of the code. Writing tests for these early gives clarity to their expected behaviours and outcomes and so makes the writing of the code itself more straightforward and rabbit hole-free.
A thorough and clear set of unit tests which are established early on have a secondary utilisation in terms of documentation. If the tests cover all scenarios and clearly outline expected outcomes from a piece of code, they provide a very effective means of documenting how the code works. In terms of maintenance and on-boarding, such documentation is invaluable.
expect object, which has access to a very wide range of methods that cover the vast majority of scenarios. Examples of such assertions include:
Jest also provides a comprehensive mocking library which allows the developer to abstract functionality for testing. These include timer mocks, manual mocks, ES6 class mocks and bypassing module mocks.
A third great feature that is provided by Jest is Snapshot Testing. This checks to see if the outcome of a given test matches a previously saved outcome. If not, then the test will fail.
As outlined above, Test-Driven Development brings a lot of additional benefits to an application, apart from basic testing. The quality of tests due to the early determination of objectives and scenarios increases confidence and reliability in the software and product. It facilitates clear thinking about the code for the developer prior to getting bogged down in details. It brings in advance planning to the process in order to prevent drift in implementation. When the developer is subsequently writing the code, the tests can be regularly run in order to ensure that everything is on track. And it also provides for a form of clear documentation for each piece of functionality.
Ultimately, it makes for good software and in turn, better business.
For these reasons, Test-Driven Development is a highly recommended approach for unit testing and software development in general.