There is a lot of confusion regarding unit tests. Do we deliver faster or slower with unit tests? Do unit tests finalize the code, or do they open it up for a change? Shall we have more test code than production code, or less? Shall we allow duplication in unit tests? What are the units anyway?
Correctness of the code
As unit tests are tests, let’s take a step back, and take a look at the correctness of the code. How do we know that our code is working well?
Some people think that software correctness is a yes or no question: is it working well or not? Some folks think it’s an array of yes or no questions: we can check which features work well, and which don’t.
Others would include the code quality into correctness. They are usually software architects and senior devs who review other peoples code, and they accept it if and only if the code is well written.
In my opinion correctness of the code is a collective illusion. As long as we all agree that the system is working well, the system is indeed working well. On the other hand, if we don’t agree, then trouble starts. How should we fix? Which part should we fix? How will we know that the system works well again?
Collective illusions are not that rare
I am not well versed about epistemology. I know just enough to differentiate between real science and pseudo science. So junior programmers with liberal arts major, feel free to recommend books in the comment section.
So, collective illusions are not that rare. Moreover, that something being a collective illusion, it does not make it any less real. Say for instance, gender is by definition is social construct. Maybe you and all your friend have different opinions about it, and that brings us to the next topic: the polarization of media in general, and social media bubbles in particular.
Due to the social media bubbles, people in the same neighbourhood can live in completely different relaities. When I’m writing this, some folks are convinced that the covid pandemic is fake, it’s just a hype, a hysteria, it’s only purpose is that the government should control us. Others think that the vaccines make you sick. No amount of scientific reason would convince them otherwise. Also, this reality is quite different from mine.
Here in Hungary, there are millions of people who are afraid of refugees coming to Hungary and take their jobs. Or their welfare. Those are uneducated people, easily manipulated by propaganda. Nevertheless, their reality is quite different from mine – whereas I think Hungary is not appealing, our economy is not strong enough for that.
Back to code correctness. This is really a touchy-feely subject. I think every experienced developer has a story about a customer, who wanted to creep in a feature as bug report. Customers too have stories about developers rejecting bug reports and demanding an official change request.
If this kind of disagreements escalate, then we end up with highly trained individuals discussing documents. They read the documents, add comments, send emails about the comments, and so on. Those individuals can be business analysts, project managers, product owners, sometimes even lawyers. The documents can be bug reports, specifications, sometimes contracts. Otherwise it’s not much different than rabbis to make sense out of the Talmud (or from Sulchan Alruch).
Collective illusions are not that new
Some postmodern writers deliberately play upon our cognitive illusions. Many writers play with guest texts: they take a text out of context, and put that into their works, either unchanged or in a damaged form – so they give different meaning to the same (or almost the same) text. I know one example where the write put the same text in the same book twice, with minimal changes. The book is “Bevezetés a szépirodalomba” by Péter Esterhazy. This book is not available in English. So, junior programmers with English majors, feel free to provide other examples in the comment section.
Mass propaganda is not that new. During the 20th century there were two big totalitarian regimes, the Soviet Union and the Nazi Germany, the Third Reich. These two inspired George Orwell to write 1984.
Bending the truth is not as new as 20th century. In the 17th century the Catholic Church imprisoned a scientist for disseminating the truth about our Solar System. At that point people believed that the Sun revolved around the Earth, but this is not important right now. It is more important that the Catholic Church new that Galilelis findings could have changed the peoples worldview, i.e. they would change a collective illusions. Collective illusions are real. (On the other hand, private illusions are called delusions.)
There is some hope (back to unit tests)
When we discuss the state of affairs in a real science, we are depending upon repeatable experiments. Unit tests are the experiments that programmers are writing and executing. So a unit test expresses what the programmer thins the system should do.
This is how programming connects to the real sciences. As doctors are trying to figure out whether a medicine safe and/or it works, they conduct experiments, like clinical trials. Programmers write and run unit tests. Testers also write and run tests, and that too connects software development to the real sciences. But we are focusing on unit tests now.
A practical question: What is a unit?
The term unit test is misleading. Some folks prefer another term: programming test. Still, we don’t know the unit: is it a method? Is it an object? Is it a collection of objects?
The answer is the ultimate programming answer: it depends.
When we are testing a
LinkedList, it makes sense to add stuff, then retrieve it. In this sense the unit is an object, and we test it through several methods of that object.
When we are testing a
FizzBuzz solution, we can test the
generateFizzBuzzText() method by itself. In this case the unit is a method.
Let me tell the story how I learned to program. I wrote a little code in ANSI C to load some data from file. I wrote the program, then tested it, and it didn’t work. I asked a friend to help me to fix it. He looked at my code, and he rejected to debug it. I was disappointed. But he taught me an important lesson.
He said, this is not how we write program. Then how, I asked. He said, write a little code, and then check if that works.
Well, that little code that you want to check, that is a unit.
Well, it’s not that simple. Sometimes you keep those unit tests, because they feel like finished. Sometimes you grow your minimal unit tests into a meaningful chunk and then you keep it. Sometimes you copy and modify them, in other cases you change them into a data-driven test. There is no simple way to decide what to do. This comes with experience: you write unit tests, and then see how they work out for you. If you are lucky enough, you can also look at your colleagues unit tests and learn a trick or two.
How to write unit tests?
When you look into tutorials, you will find how to add the @Test annotation, what is the difference between assumption and assertion, how can you set up data, and so on. These are important things, but I see that juniors struggle with writing testable code.
There are a handful of ideas behind writing testable code:
- If you have to decide between speed and testability, choose testability. Speed is usually not that important. In the cases it is, you can always go back to the slow parts of the code and think about it.
- You have to write one little test and one little code, just after each other. So your code will be testable. Write the tests first, if you can. If you can’t TDD just yet, don’t worry: write one little test right after you wrote just enough code to test.
- It’s OK to write short methods. Actually, it is good practice to write short methods.
Yes, unit tests change the way we write the code. This is a frequent phenomenon, that measurements change the behavior, just like a voltage meters change the voltage. Luckily, we don’t have to worry about that: in our case unit testing changes the code for the better.
Yes, but really, how to write unit tests?
A good unit test is organized into three parts: Arrange, Act, Assert. Sometimes they are called Given – When – Then. Say for intance:
- Given that the list is empty (Arrange an empty list)
- When I add one item (Act: add one item to the list)
- Then I want to see that the lists size is 1 (Assert that the size of the list is 1)
There are tools for writing unit tests. I am quite familiar with JUnit 4. JUnit 5 is around, but that is not too much different. TestNG is another test framework, not too different either. You should learn the one used in your current work environment. Nevertheless, I am linking a few tutorials at the end of the posts.
Writing unit tests is can be hard at first. So don’t worry too much if you fail at the first couple of attempts. Keep trying, and look at the tests the senior developers wrote.
Benefits of unit tests:
- As we have just discussed, unit tests make code cleaner. (We’ll cover that in another post).
- Unit tests make the distinction desired behavior (what does the system do) and incidental behavior (how the system does it). So unit tests make the system easier to change.
- Unit tests protect against regressions (when we change one part of the system, and another part breaks)
- Unit tests make us deliver faster – once we learned how to write unit tests.
Legacy code and unit tests
In this post we focused on writing new code. Quite often we see legacy code bases that don’t have too much unit tests. That is quite a different challenge, I am going to cover that in a different post.