I started writing some functions to help test assertions on maps.
Let's say I have the following map:
#{a => 0, b => 2}
And after calling a function in my business logic I expect the map to look like this:
#{a => 1, b => 2}
In other words, I expect
a
to change from 0 to 1b
unchangedI could just leave this test to pattern matching, however, if I have a large map with a lot of keys (and perhaps even a lot of submaps) it is not obvious where the problem might be - at least it takes some time to find the problem (maybe some values which I expected to be updated were not - or maybe there are some extra keys, etc, etc - I hope you get the point).
So as a start, I came up with the following helper function in my module:
-module(asserts).
-include_lib("eunit/include/eunit.hrl").
maps_assert_map_has_updated_values(BaseMap, Updates, ResultingMap) ->
BaseMapKeys = lists:sort(maps:keys(BaseMap)),
ResultingMapKeys = lists:sort(maps:keys(ResultingMap)),
UpdatesKeys = lists:sort(maps:keys(Updates)),
BaseMapWithoutUpdates = maps:without(UpdatesKeys, BaseMap),
BaseMapKeysWithoutUpdates = lists:sort(maps:keys(BaseMapWithoutUpdates)),
ResultingMapWithoutUpdates = maps:without(UpdatesKeys, ResultingMap),
[
?_assertEqual(BaseMapKeys, ResultingMapKeys),
lists:foldl(
fun(Key, Acc) -> [?_assertEqual({Key, maps:get(Key, Updates)},
{Key, maps:get(Key, ResultingMap)}) | Acc] end,
[],
UpdatesKeys
),
lists:foldl(
fun(Key, Acc) -> [?_assertEqual({Key, true},
{Key, maps:is_key(Key, ResultingMap)}) | Acc] end,
[],
UpdatesKeys
),
lists:foldl(
fun(Key, Acc) -> [?_assertEqual({Key, maps:get(Key, BaseMapWithoutUpdates)},
{Key, maps:get(Key, ResultingMapWithoutUpdates)}) | Acc] end,
[],
BaseMapKeysWithoutUpdates
)
].
I call this from another module, which is executed by eunit:
-module(map_tests).
-include_lib("eunit/include/eunit.hrl").
simple_map_test_() ->
asserts:maps_assert_map_has_updated_values(
#{a => 0, b => 2},
#{a => 1},
#{a => 1, b => 3}).
And I get the following error message:
asserts:30: maps_assert_map_has_updated_values...*failed* in function asserts:'-maps_assert_map_has_updated_values/3-fun-8-'/3 (test/asserts.erl, line 31)
**error:{assertEqual_failed,[{module,asserts},
{line,31},
{expression,"{ Key , maps : get ( Key , ResultingMapWithoutUpdates ) }"},
{expected,{b,2}},
{value,{b,3}}]}
Which is almost what I want in that it reports the problematic key and the problematic values in the map, however it reports the error being in the asserts
module, even though I am interested in seeing the actual test that failed, not the reference to the assertions module. I thought test generators were good for exactly that, but I wasn't able to get this working in that way.
Is there any way in which I can make eunit
report the actual test module (map_tests
, function/test simple_map_test
line X) instead of my asserts
module?
The problem is, the ?_assert...
macros are compiled into the asserts
module, and that's where EUnit does its magic to figure out modules and line numbers (at compile time, that is). If you replace the maps_assert_map_has_updated_values
function with a macro that you put in an header file that you include, you should see the right stack trace.