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:
I'm currently writing one unit test module for each function, that way my test names are quite straight forward and take the form of should_do_desired_behaviour_test().
I started with one module for each module I was writing, but didn't feel right, as naming test became more and more unwieldy, and test modules felt disorganized.
The problem I am foreseeing with the approach I've taken is that I'm goi开发者_如何学Pythonng to end up with many modules, which need to be executed. I will then need some kind of global module to run all tests in each test module.
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.
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
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.
精彩评论