How to mock a full module? - unit-testing

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?

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;.

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

Related

Accessing a global in a perl modulino from a test

In a unit test I need to set a global variable used in a perl script that I have changed into a modulino. I'm calling subs in the modulino quite happily.
Using perl (v5.18.2) built for x86_64-linux-gnu-thread-multi, on Ubuntu.
Note the modulino is so simple it doesn't even require the "caller()" trick.
test.pl
#!/usr/bin/perl
use strict;
use warnings;
my %config =
(
Item => 5,
);
sub return_a_value{
return 3;
}
test1.t
#!/user/bin/perl -w
use warnings;
use strict;
use Test::More;
use lib '.';
require_ok ( 'test.pl' );
print return_a_value();
test2.t
#!/user/bin/perl -w
use warnings;
use strict;
use Test::More;
use lib '.';
require_ok ( 'test.pl' );
$config{'Item'} = 6;
test1.t displays as expected
ok 1 - require 'test.pl';
3# Tests were run but no plan was declared and done_testing() was not seen
test2.t (fails to compile)
Global symbol "%config" requires explicit package name at test.t line 8.
Execution of test.t aborted due to compilation errors.
As pointed out by choroba, my variables aren't global. The best solution for me, and what I should have done to start with is to add a setter sub to the modulino, something like:
sub setItem
{
$config{'Item'} = shift;
return;
}
and since I am now going to want a unit test for that, a getter is also a good idea
sub getItem
{
return $config{'Item'};
}

How to restore a mock created with jest.mock()?

Apparently mock.mockRestore() does not restore the original implementation of a mock created using jest.mock()
// a.js
export default class A {}
// b.js
import A from './a';
export default class B extends A {}
// test.js
import A from './a';
import B from './b';
jest.mock('./a');
jest.mock('./b');
const b = new B();
test('instanceOf', () => {
A.mockRestore();
B.mockRestore();
expect(b).toBeInstanceOf(A); // fails
});
mockFn.mockRestore only works for a mock function created with jest.spyOn:
const obj = {
func: () => 'original'
}
test('func', () => {
const mock = jest.spyOn(obj, 'func');
mock.mockReturnValue('mocked');
expect(obj.func()).toBe('mocked'); // Success!
mock.mockRestore();
expect(obj.func()).toBe('original'); // Success!
})
jest.spyOn wraps the original function and provides mockRestore as a way to restore the original function.
jest.mock calls work a little differently.
Jest takes over the require system and jest.mock tells Jest that it should return the module mock instead of the actual module whenever it is required.
This means that the module mock doesn't wrap the original module, it completely replaces the original module in the require system. So mockRestore may be defined on mock functions within the module mock, but calling it doesn't restore the original implementation.
jest.mock is typically used when you want to mock an entire module for the whole test.
It is particularly useful when using ES6-style import statements since babel-jest hoists jest.mock calls and they run before anything else in the test file (including any import statements):
import A from './a'; // <= A is already mocked...
jest.mock('./a'); // <= ...because this runs first
test('A', () => {
// ...
}
There isn't an easy way to restore the original module during a test that uses jest.mock since its primary use is to mock a module for an entire test.
If you are trying to use both a mock and the original implementation during the same test there are a few options:
Mock one particular function using jest.spyOn and restore it using mockRestore
Use jest.doMock to avoid the hoisting behavior of jest.mock...just note you also need to use require within the scope that uses jest.doMock instead of using a top-level import
Use jest.requireActual at any time to require the original module
Assuming you cant use spyOn,
you can do something like this:
// test.js
jest.mock('./a');
import A from './a';
A.mockImplementation(params => 'mockVal');
const actualA = jest.requireActual('./a');
test('instanceOf', () => {
A.mockRestore(); // acts like mockReset()
A.mockImplementation((params) => {
return actualA(params);
});
});

Does the Perl compiler need to be told not to optimize away function calls with ignored return values?

I am writing new Perl 5 module Class::Tiny::ConstrainedAccessor to check type constraints when you touch object attributes, either by setting or by getting a default value. I am writing the unit tests and want to run the accessors for the latter case. However, I am concerned that Perl may optimize away my accessor-function call since the return value is discarded. Will it? If so, can I tell it not to? Is the corresponding behaviour documented? If the answer is as simple as "don't worry about it," that's good enough, but a reference to the docs would be appreciated :) .
The following MCVE succeeds when I run it on my Perl 5.26.2 x64 Cygwin. However, I don't know if that is guaranteed, or if it just happens to work now and may change someday.
use 5.006; use strict; use warnings; use Test::More; use Test::Exception;
dies_ok { # One I know works
my $obj = Klass->new; # Default value of "attribute" is invalid
diag $obj->accessor; # Dies, because the default is invalid
} 'Bad default dies';
dies_ok {
my $obj = Klass->new;
$obj->accessor; # <<< THE QUESTION --- Will this always run?
} 'Dies even without diag';
done_testing();
{ package Klass;
sub new { my $class = shift; bless {#_}, $class }
sub check { shift; die 'oops' if #_ and $_[0] eq 'bad' }
sub default { 'bad' }
sub accessor {
my $self = shift;
if(#_) { $self->check($_[0]); return $self->{attribute} = $_[0] } # W
elsif(exists $self->{attribute}) { return $self->{attribute} } # R
else {
# Request to read the attribute, but no value is assigned yet.
# Use the default.
$self->check($self->default); # <<<---- What I want to exercise
return $self->{attribute} = $self->default;
}
} #accessor()
} #Klass
This question deals with variables, but not functions. perlperf says that Perl will optimize away various things, but other than ()-prototyped functions, it's not clear to me what.
In JavaScript, I would say void obj.accessor();, and then I would know for sure it would run but the result would be discarded. However, I can't use undef $obj->accessor; for a similar effect; compilation legitimately fails with Can't modify non-lvalue subroutine call of &Klass::accessor.
Perl doesn't ever optimize away sub calls, and sub calls with side effects shouldn't be optimised away in any language.
undef $obj->accessor means something similar to $obj->accessor = undef

Is it possible to unit test that my package does not import a specific package?

I want to make sure my Go package use var instances provided by a "dal" package and does not accidentally import and use db access packages directly.
I guess I can do regexp search on source but I wonder if there is a way to ensure the rule through standard Go testing?
Just to give an idea what I'm going to do:
Interface package:
package dal
type UserDal interface {
GetUser(id int) User
}
Implementation package:
package dal_db_specific
import (
"some_db"
"dal"
)
type UserDalDbSpecific struct {
}
func (_ UserDalDbSpecific) GetUser(id int) User {
some_db.executeQuery(...)
...
return user
}
register_dal() {
dal.UserDal = UserDalDbSpecific{}
}
User code package:
import (
"dal"
"some_db" <-- Fail here!
)
func someFunc() {
user := dal.User.GetUser(1) // Right way
some_db.DoSomething() <-- Fail here!
}
Slightly more reliable than grep: parse the target source using the standard parser package and inspect the AST. You'd be looking for ImportSpec nodes matching the DB access packages. Fail the test if any are found.

Running an individual test from a specification

Is there a way to run a specific test from a Specs 2 Specification? e.g. If I have the following:
class FooSpec extends Specification {
"foo" should {
"bar" in {
// ...
}
"baz" in {
// ...
}
}
}
I would like to have a way to run only FooSpec > "foo" > "bar" (using some arbitrary notation here).
You can use the ex argument from sbt to run a specific example:
sbt> test-only *FooSpec* -- ex bar
You can also mix-in the org.specs2.mutable.Tags trait and include a specific tag:
sbt> test-only *FooSpec* -- include investigate
class FooSpec extends Specification with Tags {
"foo" should {
tag("investigate")
"bar" in {
// ...
}
"baz" in {
// ...
}
}
}
You can also just re-run the previously failed examples, whatever they are
sbt> test-only *FooSpec* -- was x
Finally, in the next 2.0 release (or using the latest 1.15-SNAPSHOT), you will be able to create a script.Specification and use "auto-numbered example groups":
import specification._
/**
* This kind of specification has a strict separation between the text
* and the example code
*/
class FooSpec extends script.Specification with Groups { def is = s2"""
This is a specification for FOO
First of all, it must do foo
+ with bar
+ with baz
"""
"foo" - new group {
eg := "bar" must beOk
eg := "baz" must beOk
}
}
// execute all the examples in the first group
sbt> test-only *FooSpec* -- include g1
// execute the first example in the first group
sbt> test-only *FooSpec* -- include g1.e1
There is however no way to specify, with a mutable Specification, that you want to run the example "foo" / "bar". This might be a feature to add in the future.
You can choose the namespace of tests to be executed, but AFAIK there's no way to run a specific test from the sbt console. You can do something like:
sbt test:compile "test-only FooSpec.*", which runs the tests only from the FooSpec namespace, but this selection is namespace based and not even that works properly. This is the selection mechanism, but it somehow fails and always runs the entire set of tests found in your project.
Update
From the official documentation:
test-only
The test-only task accepts a whitespace separated list of test names to run. For example:
test-only org.example.MyTest1 org.example.MyTest2
It supports wildcards as well:
test-only org.example.*Slow org.example.MyTest1