Why should we even do testing?

Image credits xkcd

Hey there!

Since my last tutorial, I’ve talked to many devs learning React to see what was the thing that they were having the most trouble with.

The unanimous answer, without a doubt, was testing.

Not only they were confused on how to do it but on why they should do it.

So in this article, I will try my best to give my opinion on what is programming and why we should write tests

Why should we test?

This question is actually way more profound than it sounds. So I will address it as it deserves, fasten your seatbelts.

To answer it, I first want to answer this other question:

What is programming?

Well… “Programming is writing code, duh”

Yes, that’s true, but “writing code” is just the visual manifestation of “Programming”

When you sit down in your chair to write your new Tinder but for illegal street racing, many things go through your head:

You first start thinking “OK, each user of the app is a street racer, so they will have a car… oh wait, they may have more than one car each!”

Then you continue “…so they’ll be presented with a list of possible competitors which they can choose from. If both match they need to race together… oooh then in their profiles I can display a list of won and lost races, and maybe a score”.

“Obviously, before YCombinator invests in my startup, I already should have some money coming in, so I’ll invent the concept of a sponsor that can sponsor races and assign prices to winners”

Then in a sudden moment of realization, you remember that street racing is illegal and that you should maybe pivot.

BUT all the thought process that you were doing there, that as well was programming.

You were building a theory in your mind about the world of street racing.

You were sketching concepts (a driver has one or more cars, a driver can win or lose races, a race is a competition between two or more drivers, etc…), you were abstracting the real world into an artificial model in your mind.

And that —is in my opinion— the essence of programming.

Well, it’s not only my opinion. A renowned paper by Peter Naur talks about this. The article is called Programming as Theory Building. It’s 14 pages long if you want to read it.

OK ok, so if you don’t want to read it, let’s work through some of the conclusions in this blog post.

If the essence of programming is building a theory in your mind about a problem domain, what are some of the repercussions of this?

Well first of all notice that you’re building the theory in your mind. Not in space, not in the ether, and especially not in the mind of other people.

That’s why in events where a whole team leaves an organization, the organization crumbles.

Things are now “legacy” but not because they are old; it’s because nobody understands the theory behind why and how things work. The only people who did were the people who wrote it, and now they’ve left the organization.

That’s why replacing team members is so hard.

You can’t swap one javascript developer for other on a daily basis.

You can’t do it because, while code may be readable and writable by anyone, code —text on-screen— only constitutes a distillation of what a programmer has in its mind.

Given, if the code is actually good, you may be able to read it, understand it, form your own theory in your mind on how the domain functions, and continue the work from where it was left.

But if the code isn’t good, then good luck with that.

And that brings us to:

“What is good code? How can we make sure that we do write good code?”

First of all, realize that code is in constant change. Actually, your work as a programmer is to change it: add a new feature, remove an old feature, refactor something to make it faster, change the pricing formula, etc…

So code first and foremost should facilitate being changed.

This is the premise from which loads and loads of theories have stemmed: clean code, the SOLID principles, etc…

Apart from this first guideline, we can also take into account what we were talking about theory building.

Since coding is very often done in groups, code should be as easy as possible for others to understand. Where others here means people who have not formed in their mind the theory that you’ve built.

There are many more guidelines and philosophical qualities of what distinguishes “good code”,

But the two that we have here are very important:

  • Code should be as easy as possible for others to understand.
  • Code should facilitate being changed.

And what’s one thing that we can do to empower these two qualities?

Testing! That’s why testing is so important.

Let’s see

Code should be as easy as possible for others to understand.

How testing helps: Tests often exhibit very clearly how certain pieces of code are used and what output we can expect given a specific input. Tests also phrase very clearly in plain English things that should and shouldn’t happen.

Tests with heading phrases like “Creating a race with less than two drivers should throw an error” would be often accompanied by relevant code that shows how to create a race, how to pass arguments to the race constructor, and how errors are handled.

Tests with heading phrases like “The winner of a race receives the betted amount” would be often accompanied by code that shows how to create a race with two drivers, how to mark a race as finished, how to mark a driver as the winner, and how to assign funds to their account.

Look at how much we can understand from a codebase just by reading the tests.

Actually, when getting to learn a new codebase it’s extremely useful to start by reading the tests.

Let’s now move on to the next guideline:

Code should facilitate being changed.

How testing helps:

Good tests will test the what but not the how of a procedure. Good tests will test what output we get from a given input, but they won’t test step by step what is being made in our code.

For example, in our matchmaking app for illegal street racing example a good test could be:

“After running the matching algorithm, every driver has at least one other driver to race with”

And a bad test would be:

“The matchmaking algorithm works by assigning ELO points to everyone and matching them to the nearest pair”

Notice that using the first test will allow us in the future to improve the matchmaking algorithm with a much better one… maybe one that uses artificial intelligence!

By having written that first test, but changing the matchmaking algorithm we can now be sure that our new algorithm also always fulfills the property of making each driver have at least one other driver to race with.

What’s can we extract from this?

Testing allows you to refactor with confidence.

If you manage to build a robust test suite, that covers most of your business use cases, but that it isn’t too much tied to your current specific implementation, then voilá, you’ve got yourself a very nice codebase.

You’ll be able to make changes back and forth, improve algorithms speeds, add features, remove redundant code, etc…

All because every time you make a change, you run the test suite.

And if the test suite passes, then you know everything is good and the business will work fine.

A good test suite acts as a guardrail for you to increase your coding speed.

Why are bullet fast trains fast? Because they have rails on which they can move on. They don’t need to be looking at the GPS all the time, they can just go faster and faster.

Same for you: while the test suite prevents you from screwing up, try implementing that fast inverse square root hack to your code to see if it works. Did it work? great! It didn’t? great as well, since you caught that during development and not in production.

So to summarize:

  1. Testing allows you to refactor with confidence.
  2. A good test suite acts as a guardrail for you to increase your coding speed.
  3. Tests are a good way of documenting code for newcomers
  4. Testing allows you to diminish the risk of having all the theory and knowledge of the domain of the app only in your mind

Leave a Comment

Your email address will not be published. Required fields are marked *