erlangeunit

Eunit assertions error reporting the wrong module


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

I 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?


Solution

  • 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.