stantona
stantona

Reputation: 3300

Best practices/conventions for writing Erlang unit tests using eunit

I'm currently learning Erlang and was wondering about what are some good practices or conventions for unit testing? I am currently using eunit.

To frame this question:

So I guess I'm not quite sure what approach to take, or if I'm even remotely on the right track with this. How do you typically manage unit tests?

Cheers.

Upvotes: 30

Views: 7044

Answers (2)

dijxtra
dijxtra

Reputation: 2751

Adam gave you one approach. The other approach (the more common one, in my limited experience) is to put unit tests at the end of each module, like this. That way you group module and it's test code together in the same file. If names of your test functions end with "_test", EUnit will automagically recognise them when you call eunit:test(module) or module:test() (you don't have to write module:test() function by yourself, EUnit exports it for you).

As for multiple test functions: you don't have do to that if you don't wish to. You can just stack all of the test cases in the same function if you feel like it, for example:

whatever_test() ->
    234 = foo(bar),
    345 = foo(baz),
    [...]
    foobar = quux(baz).

And that's it. Some people like to put evey test in its own function, some stack it up in just one function. I usually group similar tests in one function and end up with 3-4-5 test functions. See which works for you.

As for "global test module", again, Adam gave you a good advice. And, again, there is another option: rebar. It is a tool which helps you testing erlang applicaitons (and much more). See here for tutorial: http://vimeo.com/8311407 Rebar will simply autodetect your test functions inside modules and run those for you.

Upvotes: 13

Adam Lindberg
Adam Lindberg

Reputation: 16587

The by far most common approach is one test module per application module. If you name your modules correctly, EUnit will find and run all your tests for you, no need to create a global module that runs tests. For example, given:

  • src/
    • meck.erl
    • meck_mod.erl
  • test/
    • meck_tests.erl
    • meck_mod_tests.erl

If you run eunit:test(meck) it will detect (if on your path) the module meck_tests and run meck_tests:test(). The test() function is automatically inserted into the module by EUnit when you include eunit.hrl.

As for naming conventions, I usually end up with something along these lines:

-module(my_tests).

-export([functiona_should_do_this/0]).
-export([functionb_should_do_that/0]).
-export([functionb_should_not_crash/0]).
% etc

If you want good names that show up in the test runs, use EUnit's test generator capabilities:

all_my_test_() ->
    [{"Should not break X",  fun first_test/0},
     {"Should perform Y", fun other_test/0}].

Any function ending with test_ tells EUnit that it should return a list of tests (this is called a test generator). A list of tests can consist just of a list of funs, a list of tuples where the first element is a string description of the test, or a more complex setup:

advanced_test_() ->
    {foreach, fun setup/0, fun teardown/1,
     [{"Assert X", fun test1/0}]}.

This will run setup/0 before each test case, and teardown/1 after each test case . The argument to teardown/1 is the return value from setup/0. You can name these functions anything you want.

There's comprehensive documentation on how to use EUnit available here.

Here's how my test module looks like: https://github.com/eproxus/meck/blob/master/test/meck_tests.erl

Upvotes: 32

Related Questions