Testing Across Applications with EUnit
I am currently developing a distributed system for my Bachelor Thesis which utilizes a node controller application to distribute calculations from a client application towards node applications. For testing I wanted to use EUnit, mostly because that’s what I already knew and writing Unit Tests for my data module was easy and straightforward.
When it came to testing how the applications played together however I ran into a wall. For a project of this size it did not really make sense to specify a new ‘collecting’ application, which would list the others as dependency and write my tests like that. Also i wanted to keep my simple project structure (‘proj_name/node_controller’, …) which then containes the Erlang OTP applications.
The solution I came up with for now is an escript that forces a compile via mode(compile).
. The other thing I added was compile([nowarn_unused_function]).
because the compiler will complain about the unexported functions. The main
function then simply calls eunit:test(?MODULE).
to execute all specified tests. Combined with a Makefile that ensures the ‘subprojects’ are freshly compiled I can now easily write my test cases.
#!/usr/bin/env escript
-module(dist_calc_tests).
-compile([nowarn_unused_function]).
-mode(compile).
-include_lib("eunit/include/eunit.hrl").
main(_Args) ->
eunit:test(?MODULE).
basic_node_controller_test_() ->
{foreach,
fun startup_basic/0,
fun shutdown_basic/1,
[
fun test_basic/1
]}.
startup_someapps([]) ->
self();
startup_someapps([{PathToApp, AppName}|OtherApps]) ->
{ok, CurrentCwd} = file:get_cwd(),
ok = file:set_cwd(PathToApp ++ "/ebin"),
ok = application:load(AppName),
ok = application:start(AppName),
ok = file:set_cwd(CurrentCwd),
startup_someapps(OtherApps).
shutdown_someapps([]) ->
ok;
shutdown_someapps([AppName|OtherApps]) ->
ok = application:stop(AppName),
ok = application:unload(AppName),
shutdown_someapps(OtherApps).
startup_basic() ->
startup_someapps(
[
{"./node", node},
{"./node_controller", node_controller},
{"./client", client}
]).
shutdown_basic(_Pid) ->
shutdown_someapps([client, node, node_controller]).
test_basic(_Pid) ->
timer:sleep(1000),
[
?_assertMatch(
[
{"127.0.0.1:13832", {
node_controller_node,
"127.0.0.1",
13832,
_OpSet,
_Timer,
online,
0
}}
], node_controller_data_node:get_nodes()),
?_assertEqual(
"login_success",
client_app:login("alice","12345"))
].
Please keep in mind this is only intended for very basic protocol testing, i.e. if i break something during refactoring or similar activities. In the future I will probably modify the startup functions so that I can also easily test out different env settings for more advanced scenarios.