A unit test is a well-specified behaviour for a unit.
A unit, since you ask, is cohesive behaviour with no uncontrolled dependencies. The article I'm writing now can only gently breach the topic of testing. For the purpose of this article that is the definition I'm going to use.
A unit test is effectively done when you've written a well specified test name. Bold statement perhaps, but with a good name the implementation is easy to write. A well specified name is a well-specified behaviour, probably of the form (but not limited to):
{thing} does {behaviour} when {condition}
As with many how-to articles, this a nice form to use when you don't know where else to start. It isn't the holy grail of formats because there is no such thing. Once you get used to writing well specified behaviours, you'll find a style that works for you and your team.
The expressive power of a name is something that tells you what effect should occur, what should be affected by that effect, and under what conditions that effect should be expected. That's it. If you can write this down then you have unit tested your functionality. Everything else after a well specified statement of intent is just implementation.
Naming is really important. Remember that the two hardest things in computer science are:
Consider the following problem statement for a simple program:
What should this program do? How is this program implemented? Fortunately, it's quite simple, but before we get to how to implement the code, let's talk about writing some behaviour statements of what to expect. Let's name our functionality "PrimePrinter", and stipulate it's behaviour with the following unit tests:
TEST(PrimePrinterPrintsTheWordPrimeWhenTheInputValueIsAPrimeNumber)
Recommended by LinkedIn
And.. Well, that's about all. The code then almost writes itself (assuming a vaguely pseudocodey syntax):
void PrimePrinter(int i) { print "prime"; }
The test code similarly is easy. Just do exactly what the test name suggests:
ASSERT(PrimePrinter(7) == "prime");
Thank you for reading. Oh? You don't think we're done? Hmm. Nope. Looks good to me. All of the behaviours stated in the problem statement are covered, so I'm not sure what we missed.
Ah, you're absolutely right. We missed the discussion to ensure we had a common understanding of the problem statement. How silly of us. You assumed that it would do something different when the number wasn't prime? You further had expected that we would have to test non-prime numbers and perhaps even more than one prime number. I guess I can see how you might think so but that's not technically in the problem statement.
When you state all of the behaviours, you tend to notice what's missing. You can also be a lot more explicit about where assumptions stop. The simplest code that prints the word prime when a number is prime with no other restrictions is what I provided. The thing is, this might be right. If that's all it has to do and there are no other limitations, this is likely a valid implementation of PrimePrinter.
Unit testing as a topic is a really big space. There are many nuances, tips for execution, and arguments to tear down unfortunate impressions of value or lack thereof. The takeaway from this short article is that unit tests have some of their greatest value with words, not code. In specifying a good set of behaviours, you and every other person who reads those test names will know what to expect. When the behaviour is explicit both in what is and is not expected, discussions will inevitably occur. Discussions to resolve ambiguities are key to making sure that all stakeholders know what is being developed. The silliness above with prime numbers will result in discussions and likely more unit tests that qualify what should and should not happen with PrimePrinter. The only "right" answer is whatever the stakeholders agree should be the behaviours, and that can only happen with discussion.
Should you unit test? Absolutely. If I only had one chance to convince you of the value of unit testing, this is my shot. If you've experienced any of these things, you should be able to appreciate the value of discussion:
Unit tests won't fix all such problems but it will prevent many of them. Better, it will set up a team to be a place where discussions to resolve expectations are commonplace. You'll be better off for it.
Outstanding Andrew Dennison. This short article really hits on most of the central points!