Recently, I was working on a number to word converter. I built a simple web API, and wired up a less-than-flattering piece of UI for it (for demonstration purposes). It works by way of you putting in a number, and then hitting a button to get your conversion. You can check it out here, although it might take a minimum of 30 seconds before it opens. It doesn't have a quick load time because I used render. Sadly, Heroku doesn't have a free tier anymore. For the project, I used a test suite called "Jest", so my references will come from Jest, but it works pretty much the same way as other testing suites.
This is my first time following the principle of writing test code before actual code. Please note that anywhere in this article you see "UI", I am referring to "User Interface." Also note that the word "code" represents a broad spectrum, but with reference to "unit testing", it gets narrowed down to "functions".
My Approach
Generally, I started the project the way I start all projects: with a pen and a jotter. The idea is to properly have a documented train of thought, and to solve the problem on paper first, before I push the power button of my laptop. The twist, though, is that I had built this converter some years ago, with a different algorithm, and in a different programming language. This time around, I opted for something I felt was more efficient. Unit testing, however, showed me that the method was far from efficient.
Jest is "zero configuration", as I have heard a few developers say. I installed it in the Node code folder I wrote the backend in, and went straight to clattering on my keyboard.
The first thing about testing is that you have to be as descriptive as you can when naming each individual test, and you also have to be very friendly with the documentation. Something like:
test('Convert 0 to zero', () => {
expect(convert(0)).toBe('zero')
})
The purpose of this article isn't to teach you how to unit test, it's about showing you how much of a gamechanger it can be if you were willing to give it a chance.
Why I loved it
- First of all, I wrote down all my test scenarios in my editor, before I had the chance to forget anyone. Jest comes with a really neat "skip()" function, that allows a particular test case to be ignored, until you manually remove the function. It looks a bit like:
test.skip('Convert 10 to ten', () => {
expect(convert(10)).toBe('ten')
})
I put about 12 different test cases, and skipped all but the first. It gave me a chance to test incrementally, to allow me focus on fixing one thing at a time. And believe me, that is a gift. On the success of a test, I would remove the skip function from the next test, and check it to see if it works.
Second, as odd as it might seem, it saved me time in the long run. I actually didn't get to work on the UI until the API was ready and thoroughly tested. Normally, I'd have had to wire up the UI, or use Postman to test each use case. This would involve shuffling through different apps and making edits. Rinse and repeat. That would have been too tiring to do. But with unit testing, I could just save and the output would automatically show me if the current state matched my expectations. This means I could build everything without leaving the comfort of my Visual Studio Code editor.
The fun was just starting. It turned out that the algorithm I used had an annoying complexity. It involved me tracking a certain change in the code. On it's own, I could get it to work eventually, but it got messy to test. Then I remembered something I read somewhere on the internet, that if code is difficult to test, then it probably isn't the best of code. This led to my refactoring all the code. I didn't fancy the idea of having to do it differently, but the end product was better code than I had initially written.
There I was, feeling like a professional, and then I tried running it and got a massive error, where all my test cases failed. Jest came to my rescue again. The fact that the testing suite shows you "what it expected to get" vs "what it actually got" made it easy to spot a little mistake. I tweaked it, and voila, all my tests ran as before.
Basically, everything got to work. I thought of, coded up, and tested through a Web API without leaving my editor (except of course, to run to Google and Stack Overflow when I needed extra knowledge on something). The best part is that I was so confident in the code I wrote.
I finally put a little simplistic UI, and decided to do the overall testing. Everything worked as expected. No problems (from the API functionality), no unexpected results. I played around with it, and it just stuck.
By Unit Testing, I wrote code that was relatively cleaner. You know how they say it's good to make reusable pieces of code, that each do only one thing? Well, unit testing indirectly solves that problem because if you can't test it, then it probably isn't great code, and if you can test it, it might not be great code, but it is code that you can refactor or modify without causing any damage to your functionality.
Added benefits
So far, the benefits have been one-sided. There are some other somewhat hidden bonuses to unit testing, although you would most likely only feel the impact if/when you are working on already-existing code that was Unit tested.
Imagine this: You are asked to refactor someone else's code (or even code you wrote from a long time ago). It could be at a new job, internship, or so on. How do you know you've refactored it without breaking any previous app functionality? Well, "Unit testing". If the predecessor or previous code writer left tests in the code, then it would be easy to refactor, and check if it all works as usual. If it does, then add your own test cases in consideration for the next dev to grace your plain text.
Also imagine this: You are working on someone else's code, and the comments are not particularly helpful, and the code is a bit confusing (possibly even imperative). Your best starting point will be checking unit tests. From the assertions made, you can have an idea of what is behind what, and from the test descriptions, you can also have a better idea of what is going on. Coupled with the actual app functionality code and whatever comments are there, you can muscle your way through it.
Generally, anything that makes your life easier in the long-run is a win-win.
Conclusion
Solving problems is so much easier when you break them into simpler and smaller problems, try to build solutions incrementally to all those problems, and then test each incremental solution. At the end of the day, you will have an all-encompassing solution to your problems. Going further, I see testing as being an integral part of my life as a developer.
Top comments (0)