Showing posts with label perl software unit testing. Show all posts
Showing posts with label perl software unit testing. Show all posts

Tuesday, September 6, 2011

YAAACCC

(Yet Another Argument Against Complete Code Coverage)

It's been said many times, but still bears repeating, that 100% code coverage in unit tests is not a good goal. If you don't already believe that, I probably won't convince you. I'm not going to rehash all the reasons. Look around, you can find lots of eloquent arguments already in print.

But I will give you one that is perhaps less obvious and less frequently cited than some, with a concrete example: code coverage software is just that, software. It is as unlikely to be completely bug-free as any other software.

Case in point: Devel::Cover is a nice package. I really like the color-coded HTML output annotating the code, and showing various coverage statistics, with drill-down. But at work, I frequently encountered low Condition coverage.

I was writing a lot of code that looked something like this:

sub twiddle { 
  my ($self, $c, $user, $device, $knob) = @_; 
  $user = decode_param($user) || return $self->error(400); 
  $device = decode_param($device) || return $self->error(400); 
  $knob = decode_param($knob) || return $self->error(400); 
  return $self->error(401) if !authenticated($user); 
  return $self->error(401) if !authorized($user, $device); 
  my $model = $c->model('Twiddle') || return $self->error(500); 
  return $model->twiddle($device, $knob); 
}
Under Condition Coverage, for a line like $user = decode_param($user) || return $self->error(400); I would get truth tables that looked like:






Even though I had run tests for both success and failure of decode_param($user), I had only 67% condition coverage, evidently because Devel::Cover didn't understand that it couldn't evaluate the truth or falsity of the second clause of ||, since return takes execution out of the current scope.

While sometimes examining coverage output can help improve code, in a case like this, modifying the code to make the coverage tool happy would just be stupid. And, as it turns out, short-lived.

I was writing up this example to present at the Catalyst Developer's Meetup, creating the sample code above and the tests and coverage to use in my presentation, when I discovered that behavior of Devel::Cover had changed. The same code and tests in my home environment give this result:





Turns out that the behavior I described above was fixed between Devel::Cover Version 0.65, and Devel::Cover Version 0.73. This doesn't disprove my point. Rather, it reinforces it. Don't blindly assume that your coverage tool is perfect, or code to its idiosyncrasies.

Code coverage should not be used as a gating factor, or to satisfy bean-counters. It is a useful tool for the developer to ensure that what should be tested is, and nothing more.

Wednesday, August 31, 2011

Kwalitee is Job #1

I've been working on a presentation on unit testing for an upcoming Catalyst Developer's Meetup, and on some real-world unit tests for a REST interface. Eventually, I plan to post the whole presentation on slideshare, but there are a couple things worth mentioning here first.

I was flailing around on the 'net the other day, looking for some ideas on better approaches to model testing, when I ran across something unusual - a technical book worth the asking price -  Perl Testing: A Developer's Notebook.
It's a nice how-to kind of book, and well organized, so it would be great for someone new to the subject. But it also contains a few nuggets that I hadn't stumbled on elsewhere yet.

Here's one of those nuggets: Kwalitee

Open-source software is great. I've been using it for a long time. But the quality varies greatly. Sometimes, even when the software itself is great, the packaging really sucks. Installers are missing, broken, or just not intuitive. Ditto for documentation.

Perl packages from CPAN however, are generally packaged up pretty well. This implies nothing about the quality or usefulness of the software itself, but aside from the occasional broken dependency chain, getting software installed from CPAN is relatively uniform and painless, and there's at least some documentation.

This is apparently due, at least in part to CPANTS, the CPAN Testing Service, and its definition of Kwalitee, a series of metrics that check packages for the existence of things like tests and documentation (and many more - read the docs).

Thanks to Test::Kwalitee, it's easy to add those checks to your perl packages. Simply add this test to your t directory:

use Test::More;
eval { require Test::Kwalitee; Test::Kwalitee->import() };
plan( skip_all => 'Test::Kwalitee not installed; skipping' ) if $@;

Run the test:

prove --lib --verbose t/kwalitee.t
t/kwalitee.t ..
1..13
ok 1 - extractable
ok 2 - has_readme
ok 3 - has_manifest
ok 4 - has_meta_yml
ok 5 - has_buildtool
ok 6 - has_changelog
ok 7 - no_symlinks
ok 8 - has_tests
ok 9 - proper_libs
ok 10 - no_pod_errors
ok 11 - use_strict
ok 12 - has_test_pod
ok 13 - has_test_pod_coverage
ok
All tests successful.
Files=1, Tests=13, 1 wallclock secs ( 0.03 usr 0.00 sys + 0.53 cusr 0.04 csys = 0.60 CPU)
Result: PASS

Address any issues found, and you too have achieved Kwalitee.