mocked ~master
A mocking framework for the D programming language
To use this package, run the following command in your project's root directory:
Manual usage
Put the following dependency into your project's dependences section:
MockeD
A mocking framework for the D programming language.
Getting started
import mocked;
class Dependency
{
string authorOf(string phrase)
{
return null;
}
}
enum string phrase = "[T]he meaning of a word is its use in the language.";
enum string expected = "L. Wittgenstein";
Mocker mocker;
auto builder = mocker.mock!Dependency;
builder.expect.authorOf(phrase).returns(expected);
auto dependency = builder.getMock;
assert(dependency.authorOf(phrase) == expected);
Expectation setup
This library defines Mocker
data type used to create mocks.
Mocker.mock!T
returns a mock builder, Mocked!T
, which is used to configure
mock's behavior. Mocked!T.expect
has all virtual methods defined in T
, with
the same type signatures. These methods can be used to set the arguments the
methods are expected to be called with. If you don't pass any arguments, then
the call to the function will still be expected, but the arguments, it will be
called with, will be ignored.
import mocked
class Dependency
{
void say(string)
{
}
}
Mocker mocker;
auto mock = mocker.mock!Dependency;
mock.expect.say("Naturam expelles furca, tamen usque recurret. (Horace)");
// or mock.expect.say(); to ignore the arguments
mock.get.say("Naturam expelles furca, tamen usque recurret. (Horace)");
mock.get
returns the mocked object itself (child class or an interface
implementation). If mock.get.say
was called with a different string or
wasn't called at all, an error would be thrown.
If the mocked function has several overloads, and you'd like to ignore the
arguments, you have to specify explicitly, which overload is expected to be
called. You can do it by providing the types of the arguments. In the
above example you would write: mock.expect.say!string()
, since the function
Dependency.say
has a single string
parameter.
You can configure consecutive calls to Dependency.say()
by calling
.expect.say(...)
multiple times.
Stubs
MockeD provides an API for creating stubs. The API is very similar to the mock
API. Just replace Mocker.mock!T
with Mocker.stub!T
and
mock.expect.method(arguments)
with mock.stub.method(arguments)
.
The arguments won't be used to verify the expectations (there are no expectations), but to match a configured method. In the following example we configure the stub to return the respective authors (values) for different phrases (arguments).
import mocked
enum string vergil = "tu ne cede malis, sed contra audentior ito.";
enum string plotinus = "neque est alter hijus universi locus, quam anima";
class Dependency
{
string authorOf(string)
{
return null;
}
}
Mocker mocker;
auto stub = mocker.stub!Dependency;
stub.stub.authorOf(vergil).returns("Vergil");
stub.stub.authorOf(plotinus).returns("Plotinus");
assert(stub.get.authorOf(vergil) == "Vergil");
assert(stub.get.authorOf(plotinus) == "Plotinus");
The stub builder for T
is called Stubbed!T
.
passThrough
Instead of returning or throwing a given value, pass the call through to the mocked type object.
import mocked;
class Dependency
{
bool isTrue()
{
return true;
}
}
Mocker mocker;
auto mock = mocker.mock!Dependency;
mock.expect.isTrue.passThrough;
assert(mock.get.isTrue);
returns
Set the value to return when method matching this expectation is called on a mock object.
import mocked;
Mocker mocker;
auto mock = mocker.mock!Object;
mock.expect.toString.returns("in abstracto");
assert(mock.get.toString == "in abstracto");
throws
When the method which matches this expectation is called, throw the given exception.
import std.exception : assertThrown;
Mocker mocker;
auto mock = mocker.mock!Object;
mock.expect.toString.throws!Exception("");
assertThrown!Exception(mock.get.toString);
action
When the method which matches this expectation is called execute the given delegate. The delegate's signature must match the signature of the called method.
static bool flag = false;
class Dependency
{
void setFlag(bool flag)
{
}
}
Mocker mocker;
auto mock = mocker.mock!Dependency;
mock.expect.setFlag.action((value) { flag = value; });
mock.get.setFlag(true);
assert(flag);
Warning
Be aware that actions are called after they are set, so if an action is a closure and it carries some context that changes at a later point of time, it can lead to surprising behavior.
Consider the following example:
class Dependency
{
void setFlag(bool flag)
{
}
}
Mocker mocker;
auto mock = mocker.mock!Dependency;
foreach (action; [true, false])
{
mock.expect.setFlag.action((value) { assert(action == value); });
}
mock.get.setFlag(true);
mock.get.setFlag(false);
The assertion inside the action will fail,
because value
after the loop always equals to the last element of the array, false
.
In this case static foreach
can be used instead to avoid this effect,
since static foreach
generates code and value
in static foreach
is not a variable, but a manifest constant.
repeat/repeatAny
This expectation will match exactly n
times or any number of calls,
respectively.
enum string expected = "Three times you must say it, then.";
Mocker mocker;
auto builder = mocker.mock!Object;
builder.expect.toString.returns(expected).repeat(3);
// Or: builder.expect.toString.returns(expected).repeatAny;
auto mock = builder.get;
assert(mock.toString() == expected);
assert(mock.toString() == expected);
assert(mock.toString() == expected);
Configuration
Custom argument comparator
You can provide a function, which will be used to compare two objects of a
specific type.
Use Configure
instead of the Mocker
to create a custom mocker instance.
Configure
takes a tuple of "comparators", so you can specify as many
comparators as you like, but they aren't allowed to conflict, so the types in
question should be distinct types.
Every "comparator" is a function actually used for the comparison. This function should have exactly two arguments of the same type and return a boolean value.
import mocked;
import std.math : abs;
class Dependency
{
public void call(float)
{
}
}
// This function is used to compare two floating point numbers that don't
// match exactly.
alias approxComparator = (float a, float b) {
return abs(a - b) <= 0.1;
};
Configure!approxComparator mocker;
auto builder = mocker.mock!Dependency;
builder.expect.call(1.01);
auto mock = builder.get;
mock.call(1.02);
Call order
The check that all expected calls are being made in a specific order can be
disabled. Set .unordered()
on the mock object.
import mocked;
class Dependency
{
void callFirst()
{
}
void callSecond()
{
}
}
Mocker mocker;
auto mock = mocker.mock!Dependency.unordered;
mock.expect.callFirst;
mock.expect.callSecond;
mock.get.callSecond;
mock.get.callFirst;
- Registered by Eugene Wissner
- ~master released 2 years ago
- funkwerk-mobility/mocked
- MPL-2.0
- Authors:
- Dependencies:
- none
- Versions:
-
4.2.1 2022-Jun-08 4.2.0 2022-Jun-06 4.1.0 2021-Apr-27 4.1.0-rc 2021-Apr-27 4.0.5 2021-Apr-21 - Download Stats:
-
-
4 downloads today
-
24 downloads this week
-
75 downloads this month
-
1470 downloads total
-
- Score:
- 1.8
- Short URL:
- mocked.dub.pm