In this section, we will discuss the Right-BICEP acronym, as proposed by Jeff Langr, Andy Hunt, and Dave Thomas, in Pragmatic Unit Testing in Java 8 with JUnit, 2015.
Just as important as knowing how to write tests, is knowing which tests to write.
Right-BICEP provides you with the strength needed to ask the right questions about what to test.
You should first and foremost validate that your code produces the expected results.
In the context of software or information modeling, a happy path (sometimes called happy flow) is a default scenario featuring no exceptional or error conditions.
"If the code ran correctly, how would I know?"
Write tests for happy-paths.
If you don’t know how to write a test around the happy path for a small bit of code, you probably do not fully understand what it is you’re trying to build.
Nothing stops you from proceeding without answers to every last question, but you need to make decisions anyway.
The unit tests you write document your choices.
When change comes, you will at least know how the current code behaves.
Write unit tests for our Calculator
class checking some happy paths.
Happy paths for the divide(int numerator, int denominator)
method:
Happy paths may not hit any boundary conditions in the code.
Boundary condition: a scenario that involves the edges of the input domain.
Boundary conditions you might want to think about include the following.
Write unit tests for our Calculator
class checking some boundary cases.
Boundary cases for the divide(int numerator, int denominator)
method:
Sometimes you’ll be able to check behavior by applying its logical inverse:
Write unit tests to our Calculator
class checking some inverse relationships.
Any interesting problem usually has several solutions, and one chooses a solution.
That leaves the "loser" solutions available for cross-checking the production results.
Maybe the runners-up are too slow or inflexible for production use, but they can help cross-check your winning choice, in particular, if they’re trusted and correct.
Write unit tests to our Calculator
class cross-checking some operations:
5 * 3 = 5 + 5 + 5
2^4 = 2 * 2 * 2 * 2
The existence of a happy path suggests that there must be an unhappy path.
You should write tests that force errors to occur, such as
You should ideally simulate environmental situations that can break your program such as
Good unit testing isn’t simply exhaustive coverage of the obvious logic paths through your code.
It’s also an endeavor that requires you to pull a little creativity out of your rear pocket from time to time.
Write unit tests to our Calculator
class that forces some error conditions. These may include
"Bottlenecks occur in surprising places, so don’t try to second guess and put in a speed hack until you have proven that’s where the bottleneck is."
"Premature optimization is the root of all evil."
"More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason - including blind stupidity."
If performance is a concern, you may want to write specific tests for it.
@Test
void divideTest() {
long start = System.nanoTime();
int quotient = Calculator.divide(6, 1);
long end = System.nanoTime();
long elapsedTime = end - start;
assertTrue(elapsedTime < 1000000000, "Expected " + elapsedTime + " to be less than 1 second");
}
A few cautions are called for.
If performance is a key consideration for you, you may want to use specific tools for the job.