Pierre
Pierre

Reputation: 2090

How to mock a full module?

I have a module B that I want to test. This module depends on the module A that I don't want to test. Here is an example of my package B:

use strict;
use warnings FATAL => 'all';

use A;

package B;
    
sub to_test {
    return A::a();
}

In my unit test, I would like to call B::to_test but I want to manually specify what value A::a() must return. To do that, I have written a module MockA.pm that contains all the subs of A to mock:

use strict;
use warnings 'FATAL' => 'all';

package Mock::A;

sub a {
    return "From mock";
}
    
1;

I know I can easily mock A::a using Test::MockModule, the problem is the package A may contain a lot of subs and Test::MockModule lets me mock them only one by one.

How can I mock the full A package with MockA?

Upvotes: 1

Views: 299

Answers (2)

ikegami
ikegami

Reputation: 385764

The existing answer shows how to replace parts of a module, which is quite sensible.

But if you wanted to replace the module entirely (to the point that the original doesn't even need to be installed), you can use the following:

# Use the name of the package we're mocking.
package A;

...

# Tell Perl that the module we're mocking has been loaded.
$INC{ ( __PACKAGE__ =~ s{::}{/}rg ) . '.pm' } = 1;

use MockA; must appear before any instances of use A;.

Upvotes: 3

Pierre
Pierre

Reputation: 2090

It is possible to redefine the subs defined in the package A by the subs in MockA. To do that, there are three conditions:

  • MockA must define the same package than A;
  • MockA must be included after all the inclusions of A;
  • The warning redefine must not be fatal.

The file MockA.pm can be the following:

use strict;
# No fatal error if a sub is redefined
use warnings 'FATAL' => 'all', 'NONFATAL' => 'redefine';

# Override package A
package A;

sub a {
    return "From mock";
}
    
1;

And, in the unit test:

use B; # B includes A
use MockA; # MockA overrides A

B::to_test() # B calls MockA::a

Upvotes: 2

Related Questions