简体   繁体   中英

How can I mock a sub in another module?

I am having troubles mocking a subroutine in another module than where I am running the tests.

I have my tests in a file called ParserTests.pl. I am trying to test a subroutine (parse) in a module LogParser.pm

sub parse {
    my ($self) = @_;
    my $rr = $self->getRR;
    while(1) {
        my $result = $self->parseCommitSet();
        if ($result eq 2) {
            last;
        }
        my $printStatus = $self->printOut($result);
        if (!$printStatus) {
            say "Problem occurred with writing to output";
            return 0;
        }
        $self->setRR(ReportRecord->new());
    }
    return 1;
}

I am trying to mock printOut so that it always returns true. What I am trying to do is this:

#! /usr/bin/perl
use v5.10.0;
use strict;
use warnings;
use Test::More 'no_plan';
use Test::MockObject;
use LogParser;
{other tests…}
my $mock = Test::MockObject->new();
$mock->set_true('LogParser::printOut');
my $test100FH = getTestFH($test100SetsNoPrev);
$logParser = LogParser->new($test100FH);
is($logParser->parse, 1, "im ok?");
close $test100FH;

But this test is failing. Can you tell me why and point me in the right path to get it working correctly for when I test parse()? I read up on a bunch of documentation but something like this is still a bit unclear.

The error is

Can't use an undefined value as a symbol reference at /Users/achu/Documents/workspace/Perl_Script/LogParser.pm line 241, <$fh> line 8371.
# Looks like your test exited with 25 just after 91.

That line (line 241) is inside the printOut subroutine though which means that it's not mocking that subroutine like I wanted it to. What am I doing wrong?

Test::MockModule is probably better suited to this;

my $module = Test::MockModule->new('LogParser');
$module->mock( printOut => sub { return 1 } );

This will cause LogParser to use your mocked version until $module goes out of scope.

Test::MockObject does not quite do what you want. It is good for supplying a minimally-implemented stub. But for making an instance of the class under test and selectively overriding its methods, you want Test::MockObject::Extends .

TMOE takes an instance and then lets you change what some of its methods do. In your example, you can use it to write the test thus:

use Test::MockObject::Extends;

my $test100FH = getTestFH($test100SetsNoPrev);
$logParser = Test::MockObject::Extends->new(
    LogParser->new($test100FH);
);
$logParser->set_true('printOut');

is($logParser->parse, 1, "im ok?");

close $test100FH;

You didn't provide the error message, but what you've defined is an object called $mock that contains a 'printout' method. But you're calling printout() on $logparser.

The point of MockObject is to create a very bare object, with a few methods so you can test other pieces of code in a algorithm that relies on an external object. For example, you could mock a database handle so that calling $dbh->fetchStuff() always returns on static row, so that you can test the code that consumes the row.

So without more context, I can't tell the possibilities for just creating a stub for printOut() so that parse knows about it.

That being said, I also don't understand the desire to have a test for the return value of the stubbed method.

Please read the documentation for Test::MockObject and try to understand how it works.

You are doing only the first half of what is actually required: You are creating a mock object. But this will not magically end up in your LogParser.

What Test::MockObject gives you is an object that behaves just like the object you want to mock. Of course, somebody or something still has to use that object. And this will have to be the code you are trying to test.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM