Ovid wrote:
> ----- Original Message ----
> From: Michael G Schwern <[EMAIL PROTECTED]>
> 
>> How does it know when the is_broken tests have finished and the twinkletoes
>> tests have begun?
> 
> First, I think the method name should be 'mock_once' and each sub gets 
> wrapped with Hook::LexWrap 
> or something similar.  Writing the code to see if the mocked sub has been
called is trivial.  If you
> need to have the mocked sub run more than once, list it more than once

It would seem to need intimate knowledge of the internals of whatever you're
testing.  Let's look at your example.

  sub _validate {
    my $self =shift;
    if ( $self->is_broken ) { throw_some_exception "we're broken" }
    if ( $self->username ) { throw_another_exception "we gots a bucket, er,
user" }
    if ( $self->is_inflatable ) { throw_up "a.k.a., 'unswallowing'" }
  }

  $some_class->mock_once(
    is_broken => 1,
    username => 'twinkletoes',
    is_inflatable => 1
  );

  my $obj = $some_class->new;

  dies_ok { $obj->validate } "we're broken";
  dies_ok { $obj->validate } "we gots a bucket, er, user";
  dies_ok { $obj->validate } "a.k.a., 'unswallowing'";


Now what happens if the order of things checked in the validation routing is
changed while keeping the tests the same?  A good test should weather this.

  sub _validate {
    my $self =shift;
    if ( $self->username ) { throw_another_exception "we gots a bucket, er,
user" }
    if ( $self->is_inflatable ) { throw_up "a.k.a., 'unswallowing'" }
    if ( $self->is_broken ) { throw_some_exception "we're broken" }
  }

  $some_class->mock_once(
    is_broken => 1,
    username => 'twinkletoes',
    is_inflatable => 1
  );

  my $obj = $some_class->new;

  # Fails, "we gots a bucket, er, user"
  dies_ok { $obj->validate } "we're broken";

  # Fails "unswallowing"
  dies_ok { $obj->validate } "we gots a bucket, er, user";

  # Fails "we're broken"
  dies_ok { $obj->validate } "a.k.a., 'unswallowing'";


Also consider what happens should _validate() call a mock_once method twice.
Or since the mocking is class-wide if a method is called on another object?

The setup is very brittle because it uses a single setup for three tests.
What you really want is something lexical where each setup is explicitly
related to the test.  This is easier to read, more robust and more generally
useful.

  {
    my $sentry = $some_class->tmp_mock( is_broken => 1 );
    dies_ok { $obj->validate } "we're broken";
  }

  {
    my $sentry = $some_class->tmp_mock( username => 'twinkletoes' );
    dies_ok { $obj->validate } "we gots a bucket, er, user";
  }

  {
    my $sentry = $some_class->tmp_mock( is_inflatable => 1 ):
    dies_ok { $obj->validate } "a.k.a., 'unswallowing'";
  }


PS  Its a strange validation routine that dies on the presence of certain
values rather than their absence.  How will mock_once() work with that sort of
validate()?

Reply via email to