This post originated from an RSS feed registered with Java Buzz
by Chris Winters.
Original Post: Shuffling between Java and Perl: Testing libraries
Feed Title: cwinters.com
Feed URL: http://www.cwinters.com/search/registrar.php?domain=jroller.com®istrar=sedopark
Feed Description: Chris Winters on Java, programming and technology, usually in that order.
I code Java for work during the day, Perl for fun during my spare time
(Sometimes I get to use Perl during the day as well, but not as a
rule.) People who first get into a language with very different
naming/formatting rules can complain angrily about them -- every once
in a while there's a post bitching about Java syntax in Perlmonks. And try googling for people hating Perl syntax...
Some people get stuck on the sigils, camel-case vs. underscores,
dot vs. arrow, etc. It's just syntax, and you do it for long enough
you get used to it.[1] (And if you can't get used to it, maybe you
should find another profession.) If I've been doing a lot of
one vs the other for an unbroken stretch it may take my brain 15
seconds to adjust. Big deal.
However, one thing my little brain keeps tripping over is a seemingly inocuous difference in arguments used in standard testing
libraries. Both the libraries have a set of simple assertions but they
order the arguments to those assertions completely differently.
So in Perl we have
Test::More
which has a set of assertions organized like this:
my $name = 'Frobozz';
is( $name, 'frobozz' );
like( $name, qr/boz$/, 'Wizard name matches pattern' );
When the tests fail you'll see:
not ok 1
# Failed test (sample.t at line 5)
# got: 'Frobozz'
# expected: 'frobozz'
not ok 2 - Wizard name matches pattern
# Failed test (sample.t at line 6)
# 'Frobozz'
# doesn't match '(?-xism:boz$)'
# Looks like you failed 2 tests of 2.
Onto Java. JUnit is the
standard Java testing library. Its asssertions look like this:
assertEquals( expected, actual );
assertEquals( optionalMessage, expected, actual );
The Java implementation of the above Perl test might look like
this:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import junit.framework.TestCase;
import junit.textui.TestRunner;
public class SampleTest extends TestCase {
private String name;
public void testWizardMatch() {
assertEquals( "frobozz", name );
}
public void testWizardPattern() {
Matcher m = Pattern.compile( "boz$" ).matcher( name );
assertTrue( m.find() );
}
protected void setUp() {
name = "Frobozz";
}
public static void main( String[] args ) {
TestRunner.run( SampleTest.class );
}
}
(In practice I'd put both name checks in the same method, but I
wanted to be able to show failures for both.)
And when those assertions fail you'll see:
.F.F
Time: 0.01
There were 2 failures:
1) testWizardMatch(SampleTest)junit.framework.ComparisonFailure: expected: but was:
at SampleTest.testWizardMatch(SampleTest.java:10)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at SampleTest.main(SampleTest.java:23)
2) testWizardPattern(SampleTest)junit.framework.AssertionFailedError
at SampleTest.testWizardPattern(SampleTest.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at SampleTest.main(SampleTest.java:23)
FAILURES!!!
Tests run: 2, Failures: 2, Errors: 0
(Discussion of which error message is more helpful or can be more
easily duplicated to run in a
standard test harness
is reserved until later, as is a rant about the paucity of assertions
in JUnit...)
Part of the reason is built into the language -- because perl
routines can take a variable number of arguments you typically put
them at the end so you can do:
sub my_routine {
my ( $required_foo, $required_bar, $optional_msg ) = @_;
$optional_msg ||= 'some default value';
}
Since Java has overloading this is less common, although I do think
it's weird to have optional arguments at the front of an overloaded
method. (But some OO overlords wrote this, so it must be good. I kid
because I love, really.)
The sneaky part is that when you mixup the 'expected' and 'actual' order, everything will still work! The only way you know is from the
assertion's failure message and, assuming you have not been doing TDD
and getting frequent assertion failures, you've probably written a
while bunch of assertions using the wrong order when you finally do
hit a failure. Doh!
[1] That said, if you're going against the standard conventions in
a language you'd better have a damned good reason, especially if those
conventions are
well-documented.
I'm looking at you, CPANmodules that use
camel-case!