Coder Social home page Coder Social logo

mocked's Introduction

MockeD

CI License Dub version Dub downloads

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;

mocked's People

Contributors

belka-ew avatar feepingcreature avatar

Stargazers

Filipp Chertiev avatar o3o avatar  avatar

Watchers

 avatar  avatar  avatar

mocked's Issues

post factum verification

This feature would allow to record called void functions and check expectations afterwards.

Mocker mocker;
auto dependency = mocker.observe!Dependency;

// Execute
dependency.foo(5);
dependency.foo(6);

// Set expections
dependency.observe.foo(5);

// Verify
// foo(6) was never observed

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.