Unit tests test a "unit" - generally a function/method, but maybe a class or even an entire module in some cases. But yes, the idea is that the test provides a known input (Pig A
in your example) and verifies that it gets the expected output (Bacon with X=1,Y=2
). Different test frameworks have different ways of verifying the outputs, but it's usually some kind of Assert
statement. In most cases, several tests will all test the same method, with different parameters (or the tests themselves may be parameterized) - your example may have tests using Pig A
, Pig B
, and Pig C
with different properties and thus different expected outputs. A whole different set of tests will test other methods in the class / module (maybe you have a method for MakeRibs()
too)
Code coverage is a measure of how many / which lines of code get executed during your tests. It's usually expressed as a percentage of total lines of code, and may be broken down by module, class, and method. One major caveat with code coverage metrics is that it only measures how many lines of code are covered, not whether those lines of code are important. Refactoring can also cause confusion with code coverage results.
Consider in your example some pseudo-code:
public Bacon MakeBacon(Pig pigToSlaughter) {
if (pigToSlaughter.isBabyPig) {
return null;
}
KillThePig();
CutPigInMeatSlices();
SmokeBaconSlices();
}
If you only ever provide Pigs
to your tests that are adult pigs, the tests will never execute that second line (returning null
), leading to code coverage of 80% for this method. Provide a baby Pig
in your test parameters and you now have 100% test coverage for this method. Depending on what the other methods in the class do, your test coverage for the class may still be dismal, though.
Now consider a slightly different version of your example code:
public Bacon MakeBacon(Pig pigToSlaughter) {
if (pigToSlaughter.isBabyPig) {
WashPig();
DryPig();
CurlPigsTailMore();
PutBowOnPig();
ReturnPigToSty();
return new Bacon(weight=0);
}
KillThePig();
CutPigInMeatSlices();
SmokeBaconSlices();
}
Now, if your tests don't provide a baby Pig
as a parameter, your code coverage for this method drops to 40% - because 6 out of 10 lines are never executed in tests. And the metrics for code coverage on your class are even worse.
Code coverage metrics can be useful as a tool in the build pipeline, but should be used with caution. It's very easy to write pointless tests solely to satisfy some benchmark (whether a minimum coverage that's part of a build step, or some metric-happy manager). On the flip side, it's also helpful to verify that vital methods / classes / modules are tested appropriately.