Unit Testing: A Simplistic and Language-Agnostic Approach
Nowadays, unit testing is a required skill for a Software Engineer. But, many of them don’t know that, and think that it’s something from another world.
The fact is:
Unit tests are just some lines of code wrote to ensure that other lines of codes are working as expected.
These tested code lines are the features of your application. That’s it, there’s no kind of magic.
I don’t wanna scare anyone. So, I’ll not use libraries, standards, classes, and keywords. I’ll try to explain unit testing in a simplistic way. Using pure code, without any importing of classes or libraries. The examples are in Python.
Motivation
Why we have to do unit tests? Unit tests are the simplest way to avoid or warning you of problems. Like when some code change breaks another functionality of the application.
Let’s see a code for example:
As we expected, the function get_company_as_string works fine. Here is examples of some values and the respective returns:
In [1]: get_company_as_string('Samsung')
Out[1]: 'Name: Samsung | Founders: Lee Byung-chul.'In [2]: get_company_as_string('Apple Inc.')
Out[2]: 'Name: Apple Inc. | Founders: Steve Jobs, Steve Wozniak and Ronald Wayne.'In [3]: get_company_as_string('Microsoft')
Out[3]: 'Name: Microsoft | Founders: Bill Gates, Paul Allen.'In [4]: get_company_as_string('XPTO')In [5]:
But, if, we need to create a function that return the sum of ages of founders on the company foundation? So, we need to adjust the data structure so that the list of founders keep their age.
After that, we have the following code. That is the base of the previous code, with a modification in founders structure and a newer function:
And, when we run our new function, we have this results:
In [1]: get_sum_ages_of_company_founders('Samsung')
Out[1]: 26In [2]: get_sum_ages_of_company_founders('Apple Inc.')
Out[3]: 87In [3]: get_sum_ages_of_company_founders('Microsoft')
Out[3]: 41In [4]: get_sum_ages_of_company_founders('XPTO')In [5]:
So, the implementation of the new feature was a success, and we can deploy a new version of our application. Is it? No no no!
Let’s execute the function get_company_as_string. Notice that we didn’t change that function, and we already verified that it was working fine. Let’s see if it keeps going well:
In [6]: get_company_as_string('Apple Inc.')
Out[6]: "Name: Apple Inc. | Founders: [{'name': 'Steve Jobs', 'age': 21}, {'name': 'Steve Wozniak', 'age': 25}, {'name': 'Ronald Wayne', 'age': 41}]."
Surprise! The results are not the same, and the function doesn’t work anymore as we expected.
Yes, it’s a simple example, but, imagine if our application had more than 100 or 1000 functions. How can we ensure that all functions keep working as we expect? Or get an alert when something goes wrong?
You know the answer: unit tests!
Assert/Expect
That’s the base, and probably, the simplest thing of unit testing. It’s kind of a little talk with the function. We tell something to the function and verify if the response is what it should be.
For example, how can we test a function that sums two numbers? Give to the function two numbers. Compare the return with the sum of those two numbers, that we already know. If the function is wrong, the comparison will be fail.
We know that 2 + 2 is 4, -3+-2 is -5 and -1+3 is 2. So, if we send 2 and 2 to the function, we expect receive 4. If we send -3 and -2, we expect receive -5 and, if we send -1 and 3, we expect receive 2. If one of them is different of what we expect, the function is wrong. It’s simple, right?
Let’s see how the unit testing could have helped us in our previous refactoring. Remembering: I’m not using any specific library or class. I wanna simplify the explanation and keep it agnostic.
In the old version of application code, the function get_company_as_string was working fine. At this point we knew some stuff:
- If we tell ‘Apple Inc.’ to the function, we expect to receive ‘Name: Apple Inc. | Founders: Steve Jobs, Steve Wozniak and Ronald Wayne.’, and
- If we tell ‘XPTO’ we expect to receive None.
So, in the coding-way:
- get_company_as_string(‘Apple Inc.’) should be equal to ‘Name: Apple Inc. | Founders: Steve Jobs, Steve Wozniak and Ronald Wayne.’, and
- get_company_as_string(‘XPTO’) should be equal to None.
Here is the code:
… and the execution of tests:
In [1]:
...: if get_company_as_string('Apple Inc.') == 'Name: Apple Inc. | Founders: Steve Jobs, Steve Wozniak
...: and Ronald Wayne.':
...: print('Success on test #1.')
...: else:
...: print('Error on test #1.')
...:
...: if get_company_as_string('XPTO') is None:
...: print('Success on test #2.')
...: else:
...: print('Error on test #2.')
...:
Success on test #1.
Success on test #2.
Now, we have the code that ensure the proper functioning of the function. So, we can test the functionality after the implementation of a new feature.
In [1]:
...: if get_company_as_string('Apple Inc.') == 'Name: Apple Inc. | Founders: Steve Jobs, Steve Woznia
...: k and Ronald Wayne.':
...: print('Success on test #1.')
...: else:
...: print('Error on test #1.')
...:
...: if get_company_as_string('XPTO') is None:
...: print('Success on test #2.')
...: else:
...: print('Error on test #2.')
...:
Error on test #1.
Success on test #2.
We got an error. Or would that be a success? The changes that we did when implemented new feature broke the function get_company_as_string. But, our tests could identify this problem, and now, we can fix that.
So, with a little change in the function, we can verify that all tests passed and ensure the quality of our software:
That was a simple approach of unit testing. In it I tried to explain the huge importance of that skill and help people that don’t know how to start in software testing.
With unit tests, you can improve the quality of software. It’s very useful to prevent unwanted side-effects on maintenance of existing code. Unit Testing can prevent a lot of issue, like software crashing and waste of time with debugging. And helps you and your teammates to maintain an software with quality. All this, with easiness and not too much of work.
I intended to talk a bit about TDD and Mock in this article, but, this article is already too big. So, it will be an another article in the future.
If you found this article helpful, give me some claps 👏.