Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing Perl module with Test::More (Intermediate Perl, chapter 14)

This is my first question to Stack Overflow. Apologies in advance if I am breaking some rules.

I have been reading Chapter 14 of Intermediate Perl, 2nd ed., which discusses testing Perl modules and using features from Test::More. I am referring to code published directly in this book in the Section titled "Adding Our First Tests".

For some background, in this chapter, a sample Animal class is created in a module with the same name. This class has a simple speak method which looks like this:

sub speak {
    my $class = shift;
    print "a $class goes ", $class->sound, "!\n";
}

The sound method is a simple string returned for an particular Animal, so for example, a Horse's sound method will be simply sub sound { "neigh" } and it's speak method should output the following:

A Horse goes neigh!

The problem I'm running into is the following: in the testing code I've created at ./Animal/t/Animal.t , I am instructed to use bare blocks and Test::More::is to test that the speak method is working. The code looks like this in the test file:

[test code snip]
{
    package Foofle;
    use parent qw(Animal);

    sub sound { 'foof' }
    is( Foofle->speak, 
        "A Foofle goes foof!\n", 
        "An Animal subclass does the right thing"
    );
}

The test fails. I ran all the Build commands, but when running "Build test", I get this failure for the Animal test:

Undefined subroutine &Foofle::is called at t/Animal.t line 28.

When I try to explicitly use Test::More::is instead of just plain is, the test still fails with the following message:

#   Failed test 'An Animal subclass does the right thing'
#   at t/Animal.t line 28.
#          got: '1'
#     expected: 'A Foofle goes foof!
# '

My methods appear to be defined exactly as I explained. I think the first error is a scope issue because of the bare blocks, but not 100% sure. The second error I am unsure about, because if I were to create a Foofle class as a child of Animal and called speak on it, I would not get a 1 response, but rather the expected output.

Would someone be able to help out on what I may be doing wrong? For perhaps relevant software versions, I am using perl v5.16, Test::More v0.98, and Module::Starter v1.58.

like image 289
rsa Avatar asked Oct 19 '12 00:10

rsa


People also ask

What is Intermediate Perl?

» Read more… Intermediate Perl shows the basics of Perl testing. We show you how to write test programs and how to run those from the command line. That’s the language-specific stuff and within the scope of the book. You can take it further though.

How do I test Perl on Windows?

You can test Perl on Windows with continuous integration through AppVeyor. I previously showed an example of with Travis CI but that was limited to Linux. You can easily set up an account through GitHub (or other means), select projects to test, and let it do its work.

Does test::more work with older versions of Perls?

NOTE This behavior may go away in future versions. Test::More works with Perls as old as 5.8.1. Thread support is not very reliable before 5.10.1, but that's because threads are not very reliable before 5.10.1.

Is the Perl documentation free to use?

This documentation is free; you can redistribute it and/or modify it under the same terms as Perl itself. Irrespective of its distribution, all code examples in these files are hereby placed into the public domain. You are permitted and encouraged to use this code in your own programs for fun or for profit as you see fit.


1 Answers

You've quite correctly explained the reason for the first error, and fixed it right (with specifying the correct package name). But you seem to miss the simple fact: speak method of Animal class does not return this a $class goes... string - it returns the result of printing it (which is 1) instead!

See, this subroutine:

sub speak {
    my $class = shift;
    print "a $class goes ", $class->sound, "!\n";
}

... does not have an explicit return statement. In this case returned is the result of evaluating the latest invoked statement of the subroutine - i.e., result of evaluating print something, which is 1 (true, actually).

That's why the test fails. You can fix it either with testing for 1 (but that's too trivial, I suppose) or changing the method itself so it will return a string that it prints. For example:

sub speak {
    my $class = shift;
    my $statement = "a $class goes " . $class->sound . "!\n";
    print $statement;
    return $statement;
}

... and, frankly speaking, both approaches look a bit... fishy. The latter one, while obviously more complete, will not actually cover all the functionality of this speak method: it tests whether the statement was correct or not only, but not whether it was printed or not. )

like image 200
raina77ow Avatar answered Sep 17 '22 07:09

raina77ow