Learning Perl Objects, References amp;amp; Modules [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Learning Perl Objects, References amp;amp; Modules [Electronic resources] - نسخه متنی

Randal L. Schwartz

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید














14.3 Writing Tests with Test::More


Like Test::Simple,
Test::More is included with the distribution
starting with Perl 5.8. The Test::More module is
upward-compatible with Test::Simple, so you can
simply change the module name to start using it. In this example so
far, you can use:

use Test::More tests => 4;
ok(1 + 2 = = 3, '1 + 2 = = 3');
ok(2 * 4 = = 8, '2 * 4 = = 8');
my $divide = 5 / 3;
ok(abs($divide - 1.666667) < 0.001, '5 / 3 = = (approx) 1.666667');
my $subtract = -3 + 3;
ok(($subtract eq "0" or $subtract eq "-0"), '-3 + 3 = = 0');

You get nearly the same output you got with
Test::Simple, but there's that
nasty little 4 constant in the first line.
That's fine once shipping the code, but if
you're testing, retesting, and adding more tests, it
can be a bit painful to keep the number in sync with the data. You
can change that to no_plan,[9] as in:

[9] You
can do this with Test::Simple as well.


use Test::More "no_plan";        # during development
ok(1 + 2 = = 3, '1 + 2 = = 3');
ok(2 * 4 = = 8, '2 * 4 = = 8');
my $divide = 5 / 3;
ok(abs($divide - 1.666667) < 0.001, '5 / 3 = = (approx) 1.666667');
my $subtract = -3 + 3;
ok(($subtract eq "0" or $subtract eq "-0"), '-3 + 3 = = 0');

The output is now rearranged:

ok 1 - 1 + 2 =  = 3
ok 2 - 2 * 4 = = 8
ok 3 - 5 / 3 = = (approx) 1.666667
ok 4 - -3 + 3 = = 0
1..4


Note that the number of tests are now
at the end. The test harness knows that if it
doesn't see a header, it's
expecting a footer. If the number of tests disagree or
there's no footer (and no header),
it's a broken result. You can use this while
developing, but be sure to put the final number of tests in the
script before you ship it as real code.

But wait:
there's more (to Test::More).
Instead of a simple yes/no, you can ask if two values are the same:

use Test::More "no_plan";
is(1 + 2, 3, '1 + 2 is 3');
is(2 * 4, 8, '2 * 4 is 8');

Note that you've
gotten rid of numeric equality and instead asked if
"this is that." On a successful
test, this doesn't give much advantage, but on a
failed test, you get much more interesting output. The result of
this:

use Test::More "no_plan";
is(1 + 2, 3, '1 + 2 is 3');
is(2 * 4, 6, '2 * 4 is 6');

is the interesting:

ok 1 - 1 + 2 is 3
not ok 2 - 2 * 4 is 6
# Failed test (1.t at line 4)
# got: '8'
# expected: '6'
1..2
# Looks like you failed 1 tests of 2.

Of course, this is an
error in the test, but note that the output told you what happened:
you got an 8 but were expecting a
6.[10]
This is far better than just "something went
wrong" as before. There's also a
corresponding isnt( ) when you want to compare for
inequality rather than equality.

[10] More precisely: you got an
'8' but were expecting a '6'.
Did you notice that these are strings? The is test
checks for string equality. If you don't want that,
just build an ok test instead. Or try
cmp_ok, coming up in a moment.


What about that third test, where
the value had to be less than a tolerance? Well, just use the
cmp_ok routine instead:

use Test::More "no_plan";
my $divide = 5 / 3;
cmp_ok(abs($divide - 1.666667), '<' , 0.001,
'5 / 3 should be (approx) 1.666667');

If the test given in the second argument fails between the first and
third arguments, then you get a descriptive error message with both
of the values and the comparison, rather than a simple pass/fail
value as before.

How about that last test? You
wanted to see if the result was a 0 or minus 0 (on the rare systems
that give back a minus 0). You can do that with the
like function:

use Test::More "no_plan";
my $subtract = -3 + 3;
like($subtract, qr/^-?0$/, '-3 + 3 = = 0');

Here, you'll take
the string form of the first argument and attempt to match it against
the second argument. The second argument is typically a regular
expression object (created here with qr) but can
also be a simple string, which is converted to a regular expression
object. The string form can even be written as if it was (almost) a
regular expression:

like($subtract, q/^-?0$/, '-3 + 3 =  = 0');

The advantage to using the string form is that it is portable back to
older Perls.[11]

[11] The qr// form
wasn't introduced until Perl 5.005.


If the match succeeds, it's a good test. If not, the
original string and the regex are reported along with the test
failure. You can change like to
unlike if you expect the match to fail instead.

For object-oriented modules, you might
want to ensure that object creation has succeeded. For this,
isa_ok and can_ok give good
interface tests:

use Test::More "no_plan";
use Horse;
my $trigger = Horse->named("Trigger");
isa_ok($trigger, "Horse");
isa_ok($trigger, "Animal");
can_ok($trigger, $_) for qw(eat color);

This results in:

ok 1 - The object isa Horse
ok 2 - The object isa Animal
ok 3 - Horse->can('eat')
ok 4 - Horse->can('color')
1..4

Here you're testing that it's a
horse, but also that it's an animal, and that it can
both eat and return a color.[12]

[12] Well,
you're testing to see that it
can('eat') and can('color').
You haven't checked whether it really can use those
method calls to do what you want!


You could
further test to ensure that each horse has a unique name:

use Test::More "no_plan";
use Horse;
my $trigger = Horse->named("Trigger");
isa_ok($trigger, "Horse");
my $tv_horse = Horse->named("Mr. Ed");
isa_ok($tv_horse, "Horse");
# Did making a second horse affect the name of the first horse?
is($trigger->name, "Trigger", "Trigger's name is correct");
is($tv_horse->name, "Mr. Ed", "Mr. Ed's name is correct");
is(Horse->name, "a generic Horse");

The output of this is:

ok 1 - The object isa Horse
ok 2 - The object isa Horse
ok 3 - Trigger's name is correct
ok 4 - Mr. Ed's name is correct
not ok 5
# Failed test (1.t at line 13)
# got: 'an unnamed Horse'
# expected: 'a generic Horse'
1..5
# Looks like you failed 1 tests of 5.

Oops! Look at that. You wrote a
generic Horse, but the string
really is an unnamed Horse.
That's an error in the test, not in the module, so
you should correct that test error and retry. Unless, of course, the
module's spec actually called for
'a generic Horse'.

Again, don't be afraid to just write the tests and
test the module. If you get either one wrong, the other will
generally catch it.

Even the use can be
tested by Test::More:

use Test::More "no_plan";
BEGIN { use_ok("Horse") }
my $trigger = Horse->named("Trigger");
isa_ok($trigger, "Horse");
# .. other tests as before ..

The difference between doing this as a test and doing it as a simple
use is that the test won't
completely abort if the use fails, although many
other tests are likely to fail as well. It's also
counted as one of the tests, so you get a "test
succeeded" for free even if all it does is compile
properly to help pad your success numbers for the weekly status
report.

The use is placed
inside a BEGIN block so any exported subroutines
are properly declared for the rest of the program, as recommended by
the documentation. For most object-oriented modules, this
won't matter because they don't
export subroutines.



/ 199