Adam Spiers <[EMAIL PROTECTED]> writes:
> Time to play devil's advocate ;-)
>
> I'm not convinced that this new Test::Unit::Assertion::* stuff is
> TSTTCPW. Admittedly this may well be because I'm missing some crucial
> advantage it gives ...
>
> What are our goals? In no particular order:
>
> 1) We would like the option of having the failure reports give us a
> listing of the assertion code that failed.
>
> 2) We would like a nice syntax for matching against regexps.
>
> 3) We (well, I, and maybe Matthew from the sound of it) would like
> some shorthand for string and numeric equality assertions that
> automatically generates a failure message of the form
> "Got `$foo', expected `$bar'".
>
> 4) We would like to be able to create new types of assertion. We
> were maybe a bit short on examples of this, but fortunately I have
> a very real need for one right now: I'm coding a new dynamic
> graphics generation suite using gimp-Perl, and I need to be able
> to be able to assert that an image generated looks exactly the
> same as one in a given filename (i.e. the fixture is image files).
>
> 5) These assertion extensions must be createable from within
> "user-space" (i.e. outside Test::Unit::*). In fact, if we satisfy
> requirements 2) and 3), I can't think of a reason why we would
> want to extend *inside* Test::Unit::*.
>
> Hope I haven't missed any.
>
> I'm not entirely convinced of the value of goal 1) -- I would have
> thought a decent failure message would be usually be adequate. If
> not, we can always figure out the file and line-number where the
> assertion is made, and pass that back to the runner to do as it
> pleases with (fire up a viewer at that line etc.).
>
> So, I propose that the following is TSTTCPW:
>
> - Pass the location (file/line) of failed assertion code to the
> listeners via the add_failure() method. This is very easy to
> implement, satisfies goal 1), and has the advantages that a) our
> assertion code doesn't get mangled by B::Deparse if we choose to
> view it, and b) we can also choose how much context surrounding
> the code we want to see. The latter is advantageous for longer
> test_*() methods.
You're assuming here that file/line is meaningful, which isn't
necessarily the case in dynamically generated code. (Like, oooh, inner
classes...) And there's nothing to stop us collecting that information
anyway.
And T::U::Assertion::CodeRef wasn't exactly hard to implement either.
I'm not sure what you mean by your (b). There's no guaranteed method
of parsing back to the beginning of the appropriate test_* method so
if you want to get the whole method you end up having to do
scattershot methods to increase the amount of context grabbed.
> - Add assert_match() and assert_no_match() methods (except with
> much better names) to T::U::A for regexp matching. This is
> trivial to do -- I've already done it, give or take -- and
> satisfies goal 2).
Pretty trivial to do using the polymorphic T::U::Assertion approach
too.
>
> - Add versions of ok() for string/numeric comparison, defaulting to
> sensible failure messages. [I'm still dying to know if using eq
> for numeric comparison ever breaks ...] Again, trivial, and I've
> basically done it already. This satisfies goal 3).
Not quite in the way that you'd expect:
print "'", '3.1415926111111112' == '3.1415926111111111', "'\n";
print "'", '3.1415926111111112' eq '3.1415926111111111', "'\n";
gives
'1'
''
And then there's
'1e1' eq '10'; # ''
'1e1' == '10'; # 1
And
$str_match = sub { $_[0] eq $_[1] or die "Expected $_[0], got $_[1]\n" }
$numeric = sub { $_[0] == $_[1] or die "Expected $_[0], got $_[1]\n" }
$self->assert($str_match, "Foo", "Bar");
$self->assert($numeric, 10, 11);
Simple, functional, doesn't add any more methods to Test::Unit::Assert
and friends. Which is nice.
> - Leave the user to code their own extended assertion techniques
> separately. We don't have to do anything at all, and it satisfies
> goals 4) and 5). Here's how I might do it for image comparison:
>
> package MyAssert::Images;
>
> sub assert_images_equal {
> my $self = shift;
> my ($image, $filename, $failure_message) = @_;
>
> # compare $image image object with image saved in $filename
>
> $failure_message ||= "image not equal to image in file $filename";
> $self->fail($failure_message) unless $is_equal;
> }
>
>
> package My::ImageTestCase;
>
> use My::ImageGenerator;
> use base qw(MyAssert::Images);
>
> sub test_button_generator {
> my $self = shift;
>
> my $button = generate_button( ... );
> $self->assert_images_equal($button, $filename1);
>
> my $icon = generate_icon( ... );
> $self->assert_images_equal($icon, $filename2);
> }
>
> This is all ridiculously simple, and AFAICS, works.
For values of ridiculously simple that end up with ballooning
interfaces I'll grant you. The 'extend assert' by reifying the
different sorts of assertion means that the basic 'assert' and
'normalize_assertion' methods can be left untouched from now until
whenever and they will continue to work with different assertion
classes.
Then, if we come up with some clever means of providing for
backtracking and better reporting, we can do that by modifying the
T::U::Assertion superclass and get that behaviour for free with the
various subclasses, which is a bonus I think.
Note too that user rolled assertions don't have to be within the
Test::Unit::* namespace, they merely have to implement a
'do_assertion' method.
So, for me, T::U::Assertion and friends are TSTTCPW because they
extend the possibilities of the system without making the interface
more unwieldy. And look at the way T::U::Assertion::Regexp almost
falls out of the structure of it, solving your issue 3 in a general
fashion.
> Now you have to shoot me down in flames and persuade me why your way
> is better ;-)
Cleaner, neater, more extensible without having to mess with any
interfaces.
--
Piers
_______________________________________________
Perlunit-devel mailing list
[EMAIL PROTECTED]
http://lists.sourceforge.net/lists/listinfo/perlunit-devel