As long the eunit documentation for test representations explains, test sets can be deep lists. Below is an example module showing the use of an outer setup
fixture whose tests are generators, each supplying an inner setup
fixture. The inner setup
fixtures correspond to your existing test suites, each with their own setup and cleanup functions. The outer setup
fixture provides the common setup and cleanup for the inner suites.
-module(t).
-compile(export_all).
-include_lib("eunit/include/eunit.hrl").
top_setup() ->
?debugMsg("top setup").
top_cleanup(_) ->
?debugMsg("top cleanup").
test_t1() ->
{setup,
fun() -> ?debugMsg("t1 setup") end,
fun(_) -> ?debugMsg("t1 cleanup") end,
[fun() -> ?debugMsg("t1 test 1") end,
fun() -> ?debugMsg("t1 test 2") end,
fun() -> ?debugMsg("t1 test 3") end]}.
test_t2() ->
{setup,
fun() -> ?debugMsg("t2 setup") end,
fun(_) -> ?debugMsg("t2 cleanup") end,
[fun() -> ?debugMsg("t2 test 1") end,
fun() -> ?debugMsg("t2 test 2") end,
fun() -> ?debugMsg("t2 test 3") end]}.
t_test_() ->
{setup,
fun top_setup/0,
fun top_cleanup/1,
[{generator, fun test_t1/0},
{generator, fun test_t2/0}]}.
Compiling this module and then running it from an Erlang shell produces the expected output:
1> c(t).
{ok,t}
2> eunit:test(t).
/tmp/t.erl:7:<0.291.0>: top setup
/tmp/t.erl:14:<0.293.0>: t1 setup
/tmp/t.erl:16:<0.295.0>: t1 test 1
/tmp/t.erl:17:<0.295.0>: t1 test 2
/tmp/t.erl:18:<0.295.0>: t1 test 3
/tmp/t.erl:15:<0.293.0>: t1 cleanup
/tmp/t.erl:22:<0.293.0>: t2 setup
/tmp/t.erl:24:<0.300.0>: t2 test 1
/tmp/t.erl:25:<0.300.0>: t2 test 2
/tmp/t.erl:26:<0.300.0>: t2 test 3
/tmp/t.erl:23:<0.293.0>: t2 cleanup
/tmp/t.erl:10:<0.291.0>: top cleanup
All 6 tests passed.
ok
The common setup runs first, then each suite runs bracketed by its own setup and cleanup, and then the common cleanup runs last.