Loading ...
Sorry, an error occurred while loading the content.

407Re: [extremeperl] unit tester

Expand Messages
  • Rob Nagler
    Aug 29, 2005
    • 0 Attachment
      Adrian Howard writes:
      > I'm reading that as not liking seeing lots of "ok BLAH" statements in
      > the test output - in which case just write a test runner that doesn't
      > show them.

      No, I don't like typing them. :-)

      > If I see duplication in my tests I refactor it. Often making lots of
      > little domain specific Test::* modules.

      Yes, that's what we do, too, but the structure is missing to allow for
      easy refactoring.

      > Can you explain what you mean here?

      Here's a simple comparison:

      use strict;
      use Test::More tests => 5;
      use Test::Exception;
      BEGIN {
      dies_ok {EMA->new(-2)};
      dies_ok {EMA->new(0)};
      lives_ok {EMA->new(1)};
      dies_ok {EMA->new(2.5)};

      in Bivio::Test this would be:

      use strict;
      use Bivio::Test;
      EMA => [
      new => [
      -2 => Bivio::DieCode->DIE,
      0 => Bivio::DieCode->DIE,
      1 => undef,
      2.5 => Bivio::DieCode->DIE,

      Note the difference. In the Test::More case, I need to specify the
      number of tests. In Bivio::Test, the infrastructure counts the test
      for me. I have to import Test::More and Test::Exception. These
      should be one module.

      You must repeat dies_ok even though it gives no new information. Just
      like the lives_ok.

      use_ok is repeated. Every test imports a class. The BEGIN is

      dies_ok is insufficient. The lack of explicit exceptions causes more
      problems in complex examples such as DB testing, where you need to
      test for constraint violations, database errors, and such.

      The tests => 5 is a denormalization. I can count the number of
      tests. The use_ok shouldn't even be a test case. If it doesn't
      import, you've got bigger problems.

      The essence of the problem is that Test::More and friends are
      imperative, and Bivio::Test is declarative. Bivio::Test can count the
      cases, and it does. Bivio::Test is 340 NCLOC in 31 subs. Test::More
      is 230 NCLOC in 26 subs. That's a small price to pay for reducing the
      number of concepts in my tests.

      > #! /usr/bin/perl
      > use strict;
      > use warnings;
      > use Our::Test::HTML::HouseStyle qw( style_ok );
      > use Our::Test::Config qw( html_files );
      > use Our::Test::Utils qw( test_all );
      > test_all { style_ok( $_ ) } html_files();

      Note the redundancies. test_all x 2, style_ok x 2, and test_all x 2;
      qw() is twice. There are three "use". use warnings should be set by
      the test infrastructure or the #! line. Here's how I would refactor
      the test:

      #!perl -w
      use strict;
      use Our::Test::HTML::HouseStyle;

      I would say that "style_ok" is implicit. That's what HouseStyle tells
      me. Our::Test::HTML::HouseStyle should subclass Our::Test::Utils.
      Our::Test::Utils should load the class under test (first arg) and
      call "html_files" on it.

      package Our::Test::HTML::HouseStyle;

      use base(Our::Test::Utils);
      sub test_all {
      my($proto, $class, $method, $args, $validator) = @_;
      $args ||= [];
      # This assumes HouseStyle has multiple validators
      $validator ||= 'style_ok';
      return $proto->SUPER::test_all(
      sub {
      return $proto->$validator(@_);

      package Our::Test::Utils;

      sub test_all {
      my($proto, $validator, $values) = @_;
      my($i) = 0;
      $proto->print('1..', @$values);
      foreach my $v (@$values) {
      my($die) = Bivio::Die->catch(sub {$ok = $validator->($v)});
      Bivio::IO::Alert->warn($v, ': invalid value',
      $die ? ('; died ', $die) : ())
      unless $ok;
      $proto->print(!$ok && 'not ', 'ok ', ++$i, "\n");

      Why use Bivio::IO::Alert->warn? Well, if $v is very large, it will
      truncate it to a configurable value. If $v is a ref, it'll
      recursively dump the value (only so far, again configurable). It
      calls ->as_string if the object can('as_string'). $die contains
      interesting information such as the entity that caused the exception,
      the line, the stack trace, etc. It's all managed for me. I can
      control the output with a simple configuration file.

      I could go on, but the point is that the tests should be as short as
      possible with as few extraneous concepts (boilerplate) as possible so
      that people won't have any excuses not to test. If I have to remember
      to import three separate classes to test my styles, I'm going to get
      frustrated and not right the test. Moreover, it's likely I've copied
      the file multiple times, which breaks OAOO and the Rule of Three.

    • Show all 33 messages in this topic