With 20 years of JavaScript experience, Riki Fridrich explains his steps in writing testable code (with the full presentation below). Not only does this allow for rapid testing for new features and updates, it fundamentally changes the way one approaches developing their own solutions.
In observations while teaching JavaScript, Riki noticed that students didn’t write tests for their code; even though everyone tells them they should.
Why is this? Their response:
- Too hard
- Too slow
- Too much work
- Oh, it sucks, too!
Often it is not writing the test that is the problem, but the code itself is not written to be testable.
But here’s the epiphany!
A focus on writing tests will influence how you write your code; it can fix problems you didn’t even know you had in your code!
During the presentation, Riki runs example tests to showcase how to debug a failed test code.
Firstly, the code needs to be readable and testable. Functions should be short, concise and only do what it’s meant to do. You must name things properly and place intermediate steps in order to make the code clear.
Next, make use of functions, as functions are highly testable (when coded with structure and discipline). The easiest scenario is that the same input will produce the same output.
Now to test the code. It doesn’t matter what tool you use for creating and running tests – they all generally work the same, but in this instance, Riki will be using Jest.
If the test-result check marks are all green, the code runs correctly. Test another code – runs correctly. Things might seem okay, but this isn’t a true test, yet.
An isolated test isn’t how code functions.
A problem may occur when all tests are run together. Isolating the tests works fine. However, there are times when running them together results in failure.
Suddenly there’s a problem. Why is that? Where was the point of failure? And how can we fix it?
Let’s add a parameter to the function, and move all outside variables in the function via parameters.
“Dirty functions”’ – as these are known – either depend on outside values or produce mutations. If you run these functions, there is a high chance they will produce different outcomes depending on something outside of the function.
The usual culprits of dirty functions are randomness, such as Math.random(), and Date.now(). However, there are others, for example, array mutations via a sort.
But we cannot live without dirty functions in our code. If there’s no dirt in the code, then the outcome will always be the same. There will be no user input, no working with dates etc.
With the understanding that dirty parts will be in our code, we need to push them to the outskirts.
The best way to do this is through parameters.
Transform dirty functions to pure functions.
We need to transform dirty functions into pure functions for the purposes of testing. In the case of randomness, you’ll need to create a default random value, and in the case of dates, you’ll define the current date, as demonstrated by Riki’s example.
It’s important to use pure functions in tests or tests won’t be reliable. For pure functions, you can be sure that if you have the same input, you will have the same output.
This coding transformation from dirty-to-pure functions isn’t just a benefit for testing. These pure functions are now cachable inside the application, which can add real performance improvements.
Let’s take an example of working on a mapping application where you need to find the shortest path between two points. This can be time-consuming each time the code runs – making the code cacheable allows the application to run smoother.
Pure functions don’t rely on anything from the outside – they can be stand-alone functions. These pure functions can also be delegated to a new Worker(), which will manage performance in a standalone thread. With this worker, we don’t need to worry about concurrency or about racing conditions. So the benefits of pure functions aren’t simply for testing.
Pure functions all the way – use them!
This demonstration so far has shown a fix to the problems that were spotted, but there are further improvements that can be made. For example, the extraction of event listeners is better managed via the test scripts, including the dispatch of events for true end-to-end testing.
To dive in deeper with Riki, watch the video and live screen recording below to see how you can improve your JavaScript, with testing as the starting point. The additional benefits come quickly, and before you know it, you’re writing cachable pure functions for serious performance enhancements, which can solve problems before you even spot them.
This is just the starting point – need to know more?
This was a brief introduction to writing testable codes at WebExpo 2023, but for a serious session for your professional development, Riki has prepared a WebExpo workshop which will be in Czech language, on May 18.
You can find out more via Riki’s workshop details page.