Every expert argues the definitions of Unit Tests. One side argues for one thing, another argues for the opposite. So let’s admit that there’s no real definition of a unit test. Now that we know we can’t define it, I will.
I don’t listen and I can’t be taught to mind my manners. So that’s what happens with a misfit and a site where I purport to be able to pontificate on programming.
The following are properties I think a unit test should have, or the things to consider when building classes on which unit tests should be run. I’ve included topics that programmers should consider, too.
Deterministic
This means that a test will give an up/down result. It either succeeds or fails. No in-between.
Idempotent
I love this word. Unit tests should not change nor should they cause a state change in the system when they run. You should be able to run a test a thousand times and it should always return the same result so long as the rest of the system state is unchanged.
RESTful queries are idempotent. They change nothing during the action.
Unit tests should change nothing during the action. This often divides the functional test from the unit test just by this property alone. Functional tests often change something or rely on other systems (like a database or network connection that can be unavailable). Functional tests are, therefore not idempotent. Unit tests must be.
Fast
Unit tests should run quickly. The whole harness of production unit tests for a large system should run in minutes, or even seconds.
Even more, when developing, a set of unit tests related to the module on which you are working should run in seconds. This way you can run it every time you change something.
- A little Coding
- Test
- A little Coding
- Test
- A little Coding
- …
Isolated
One test should not rely on the results of another test or other system actions. Each test should exist on its own.
Classes made for Unit Tests should rely on Inversion of Control
Inversion of control says that a class should keep a placeholder for a type of class that can then be passed into the instance of the object.
If a class needs a database or a service, an instance of that database (context?) should be passed into the object when it is created.
It’s a complicated topic and too much for this post. But we’ll cover it in depth when we need to use it.
Internal Code
Only run unit tests on your own code. Don’t suppose to run tests on a third-party library. (Remember Inversion of Control so you can segregate your code when it might depend on a third party library.)
After all, we’ll use unit testing to make ourselves and our product better while doing our best to use and support those third-parties who won’t destroy our product and ruin our lives.
We show some faith toward our vendors but none for ourselves.
Here’s an Argument Discussion
What do we include? It’s very difficult to test anything except public classes with public methods.
What else do we have?
We have protected and private methods. These modifiers define visibility; and test harnesses do not have visibility. Not directly.
We must build classes that use other classes. When we test those dependent classes, how will we know if the origin of our failure (because we don’t worry when we succeed) came from us the class we buried deep within the class we’re testing?
These (and other) thorny issues complicate, but do not negate our need to test. So we test, but we’ll deal with those issues when they arise.
Below, I take a quick shot at these major questions.
Public Interface
Some Unit Test Advocates teach that we should only test those methods and classes that we expose to the user. Fair enough. If our methods then, within the little black box of our logic, consume private methods and do magic and miracles to our data, our public methods will stand or fall based on the success or failure of those private methods the public methods consume. I have two issues with this:
- If I have a failure and my public method consumed five private methods, where in my six possible choices (or a permutation of those) did the failure actually occur?
- I’m a TDD guy. Writing tests for only public methods makes me a half-TDD guy. At best.
Testing Private Methods
Don’t. Rather, write your solutions such that each call to a class.function where several things can happen based on several private classes. This requires some very clean code: code with intent!
If this means calling the public interface twenty times to plumb the ranges of functionality within the public interface, then we must endeavor to write the production code that separates the duties. If that cannot be done, then we have a problem.
Remembering the Single Responsibility of a class, it should do one thing and do it well. If it does too many things, it has multiple responsibilities and is not clean. It smells.
Mocking/Faking
To the point where we must test classes that depend on external services or system resources, even our own, there is the technique of Mocking and Faking.
I won’t make the distinction here, but there is one between the two; but in this they are the same: we use stand-ins of our own design, to mimic the complexities and output of our dependencies.
Uncle Bob doesn’t like Mocking and Faking. I’m not so picky. Sometimes they are necessary and they’re usually helpful. Even Uncle Bob uses them; he just dislikes using someone else’s library to write the mock or fake.[1]
I can be wrong. I’ve said it before. I will be wrong. With sufficient opinions and public references thereof, I will be wrong. I hope I will be wrong. It gives me a chance to be corrected and learn something. So in these things, there may be differences of opinion. There are debates and discussions all over the webispheres about this (and even more esoteric trivia). But now you have my take on it.
After all, I’m following me, here. There’s no one else taking the chances.
References:
[1] First-Class Tests