Skip to content
Permalink
Browse files
Add introspection of which modules are currently mocked
There's already an `meck:expects/[1,2]`, allowing introspection into
what kind of expectations are set.
This allows one to also check which modules is meck handling at any
point.

Introduced a `foldl_mocks` internal function to reuse the same logic
for both `unload/0` and `mocked/0`
  • Loading branch information
fcristovao authored and eproxus committed May 13, 2020
1 parent 23659f6 commit 1ed7933038d709faae7d7fa8152e2c07ad2d6e50
Showing 2 changed files with 48 additions and 20 deletions.
@@ -54,6 +54,7 @@
-export([wait/4]).
-export([wait/5]).
-export([wait/6]).
-export([mocked/0]).

%% Syntactic sugar
-export([loop/1]).
@@ -460,7 +461,15 @@ history(Mod, OptCallerPid)
-spec unload() -> Unloaded when
Unloaded :: [Mod],
Mod :: atom().
unload() -> lists:foldl(fun unload_if_mocked/2, [], registered()).
unload() ->
foldl_mocks(fun(Mod, Acc) ->
try
unload(Mod),
[Mod | Acc]
catch error:{not_mocked, Mod} ->
Acc
end
end, []).

%% @doc Unload a mocked module or a list of mocked modules.
%%
@@ -723,6 +732,11 @@ capture(Occur, Mod, Func, OptArgsSpec, ArgNum, OptCallerPid) ->
capture(Occur, Mod, Func, OptArgsSpec, ArgNum) ->
meck_history:capture(Occur, '_', Mod, Func, OptArgsSpec, ArgNum).

%% @doc Returns the currently mocked modules.
-spec mocked() -> list(atom()).
mocked() ->
foldl_mocks(fun(M, Acc) -> [M | Acc] end, []).

%%%============================================================================
%%% Internal functions
%%%============================================================================
@@ -732,25 +746,18 @@ wait_for_exit(Mod) ->
MonitorRef = erlang:monitor(process, meck_util:proc_name(Mod)),
receive {'DOWN', MonitorRef, _Type, _Object, _Info} -> ok end.

-spec unload_if_mocked(Mod::atom() | string(), Unloaded::[atom()]) ->
NewUnloaded::[atom()].
unload_if_mocked(Mod, Unloaded) when is_atom(Mod) ->
unload_if_mocked(atom_to_list(Mod), Unloaded);
unload_if_mocked(ModName, Unloaded) when length(ModName) > 5 ->
case lists:split(length(ModName) - 5, ModName) of
{Name, "_meck"} ->
Mocked = erlang:list_to_existing_atom(Name),
try
unload(Mocked)
catch error:{not_mocked, Mocked} ->
ok
end,
[Mocked | Unloaded];
_Else ->
Unloaded
end;
unload_if_mocked(_P, Unloaded) ->
Unloaded.
-spec foldl_mocks(Fun, AccIn) -> AccOut when
Fun :: fun((Elem :: module(), AccIn) -> AccOut),
AccIn :: term(),
AccOut :: term().
foldl_mocks(Fun, Acc0) when is_function(Fun, 2) ->
lists:foldl(fun(Mod, Acc) ->
ModName = atom_to_list(Mod),
case lists:split(max(length(ModName) - 5, 0), ModName) of
{Name, "_meck"} -> Fun(erlang:list_to_existing_atom(Name), Acc);
_Else -> Acc
end
end, Acc0, erlang:registered()).

-spec check_expect_result(ok | {error, Reason::any()}) -> ok.
check_expect_result(ok) -> ok;
@@ -1547,6 +1547,27 @@ wait_purge_expired_tracker_test() ->
%% Clean
meck:unload().

mocked_test() ->
%% At start, no modules should be mocked:
[] = meck:mocked(),

%% Mocking a new module adds it to the list of mocked modules:
meck:new(mocked_test, [non_strict]),
[mocked_test] = meck:mocked(),

%% Mocking with implicit `meck:new` also adds to the list of mocked modules:
meck:expect(meck_test_module, c, 2, ok),
[meck_test_module, mocked_test] = lists:sort(meck:mocked()),

meck:new([foo, bar], [non_strict]),
[bar, foo, meck_test_module, mocked_test] = lists:sort(meck:mocked()),

ok = meck:unload([foo, meck_test_module]),
[bar, mocked_test] = lists:sort(meck:mocked()),

meck:unload(),
%% After unload all, no modules should be mocked again
[] = meck:mocked().

meck_passthrough_test_() ->
{foreach, fun setup_passthrough/0, fun teardown/1,

0 comments on commit 1ed7933

Please sign in to comment.