If you have been developing software for a long time, then you can easily relate to the importance of unit testing. Experts say that most bugs can be captured in the unit testing phase itself, which eventually gets passed on to quality teams. Here is a list of my top 8 signs your writing bad unit tests.
1) Test passes but not testing the actual feature
Believe me, I have seen test cases in some projects that appear to be doing lots of work in the code but in reality, they were doing nothing. For example – a test that’s sending requests to the server and no matter what the server responds with, the test was passing.
Beware of these such test cases taking place in your code repository by doing strict code reviews. Once they make their way in your code base they will become a liability on you to carry them, build them, and running them every time but without adding any value.
2) Testing irrelevant things
I have seen developers checking multiple irrelevant things so that the code passes with doing [something], well not necessarily the correct thing. The best approach is to follow the single responsibility principle, which says, one unit test case should test only one thing and that’s all.
3) Testing multiple things in assertions
In the previous sign, the test was testing irrelevant things. For this sign the unit-test is testing the correct items, however, there are too many items in one test case. This again is a violation of the single responsibility principle.
Well, please note that I am not encouraging the use of a single assertion per test case. To test a single entity, you might need to use multiple assertions, do it as needed.
For example, one API which takes some parameters in a POST body then creates the Employee object and return it in response. This Employee object can have multiple fields like first name, last name, address etc. Writing a test case to validate only first-name, then another for last-name and another for the address is duplicity of code, without any value. Don’t do it!
Instead, write a single positive test case for creating the Employee object and validate all of the fields in that one unit test. Negative test cases should be written separately doing only one thing and one assertion in this case. e.g. One test case for blank first name, one test case for invalid first name and so on. All such negative test cases can be validate using a single assertion which expect an exception in response.
4) Test accessing the testee using reflexion
This one is real bad. Trying to change the testee to suit ones need. The test will blow up when the testee may refactor some of the code. Do not use this or allow to be used either.
5) Tests swalloing exceptions
I have seen my fair share of such test cases. They silently swallow the exception in little catch block at the end of the test case. And worse is that they do not raise a failed alarm. Exceptions are signals that your applications are trying to communicate that something bad has happened and you must investigate it. You should not allow your test cases to ignore these signals.
Whenever you see an unexpected exception, fail the test case without failure.
6) Test which depends on some excessive setup
I do not like test cases that require a number of things to happen before they start testing the actual thing. Such a scenario could be an application which facilitates online meetings. Now to test whether a user of a particular type, can join the meeting, below are the steps test is performing:
- Create the User
- Set user permissions
- Create the meeting
- Set meeting properties
- Publish meeting joining information
- [Test] User join the meeting
Now in the above scenario, there are 5 complex steps which must be setup before actual logic is verified. For me, this is not a good test case.
A correct test system will have an existing user present in the system for this activity, at least. This will reduce at least two steps in the test case. If appropriate, you could have a few already created meetings to make this test case really focused.
Another way to make it correct is by using mock objects. They are there for this very purpose.
7) Test compatible to only developer machine
This is not widely seen but sometimes visible when written by freshers. They use system dependent file paths, environment variables or properties in place of using common properties/paths.
8) Tests filling log files
This never seems to be an issue until you need to perform a deep debug then make life hell for debugger who is trying to find something hidden in those log files bloated with wasteful messages.
Tests are not for debugging the application, so do not put debug statements in your logs. A single log statement for Pass or Fail is enough. Believe me.
These are my thoughts based on my experiences. I do not expect you to agree with me on all of the above points or you might have some other opinion which is perfectly cool too.