Posted on Friday, 3rd October 2008 by charpi

People coming from object oriented programming often use mock
object
in their unittest. The main question is do we need mock
when programming with erlang ?
.

The answer could be we don’t need any mock library because it’s
already part of the language
.

Let me explain a little more my vision of mock in Erlang.

The main purpose of the mock is to replace something
complicated by something very simple that you know the
behavior and that you can control. Using this mock, your tests will
only focus on one class module and forget all about the
environment of the complicated thing.
Using good unittest practices in OOP, you’ll pass all needed object in
the constructor of your classes, so you’ll be able to use mock in your
unittests.
In Erlang, we can’t use this technique because we don’t have
constructors however we can write good unittest.

  • Erlang is a concurrent programming language so we might have to
    test relationships between several processes. In order to test
    processes one by one, we can just spawn a small process in our tests
    and then pass its pid to the process under test. After that the
    only thing to do it to implement the message passing between
    processes. This technique is very close to the one used in OOP and
    their constructors.
  • Erlang is a functional programming language. One of the most
    interesting feature of FP is high order functions. It allows
    you to pass functions to your functions. Usually, high order functions
    are used to build generic algorithms but they can also be used to
    modify function behavior.
    Using fun to mock some behavior, oblige you to write some
    more tests:

    • Main function test with mock fun.
    • Production fun.
    • Real integration of the production fun in your application

Working on legacy code, it might be difficult to write tests. Of
course you can refactor to be able to use one of the previous case but
how to refactor without any tests ??. This observation make me realize
that sometimes we need something to intercept function calls.
The first technique I used was dbg. Adding traces on function
call, you can ensure that an expected side effect happened.
Anyway, if you need to setup a complete environment dbg
isn’t usable.
For those case a mock library could be useful. I have in mind
something that allow you to replace a module by another one.

There are the basic unit tests for this module

-module(mock_test).

-export([test /0]).

test () ->
    lazy_way () ,
    dynamic_way(),
    ok.

lazy_way () ->
    "i'm the production code" = my_module: who(),
    mock: replace_module (my_module, my_mock_module),
    "I'm the mocked code" = my_module: who (),
    mock: uninstall (my_module),
    "i'm the production code" = my_module: who(),
    ok.

dynamic_way () ->
    "i'm the production code" = my_module: who(),
    mock: start (),
    mock: add_module(my_module),
    mock: set_answer (my_module, who, "I'm the mocked code"),
    "I'm the mocked code" = my_module: who (),
    mock: set_answer (my_module, who, "Oops did it wrong"),
    "Oops did it wrong" = my_module: who (),

    [{my_module, who, []}, {my_module, who, []}] =  mock: calls (),
    mock: uninstall (my_module),
    "i'm the production code" = my_module: who(),
    ok.

Tags: ,
Posted in Uncategorized | Comments (4,015)

Comments are closed.