Question

I have following module

-module(bhavcopy_downloader).

-export([download/2]).

download(From, SaveTo) ->
    {ok, {{Status, _}, _, Body}} = lhttpc:request(From, "GET", [], infinity),
    case Status of 
        200 ->  file:write(SaveTo, Body),
            true;
        _ -> false
    end.

And following tests for the above code

file_download_test_() -> 
    {foreach,
     fun() ->
            meck:new(lhttpc)
            meck:new(file, [unstick])
     end,
     fun(_) ->
        meck:unload(file),
            meck:unload(lhttpc) 
     end,
      {"saves the file at specified location",
        fun() ->
            meck:expect(lhttpc, request, 4, {ok, {{200, "OK"}, [], <<"response">>}}),
            meck:expect(file, write_file, fun(Path, Data) -> 
                                    ?assertEqual(Path, "~/Downloads/data-downloader/test.html"), 
                                    ?assertEqual(Data, <<"response">>) 
                            end),
            ?assertEqual(true, bhavcopy_downloader:download("http://google.com", "~/Downloads/data-downloader/test.html")),
            ?assert(meck:validate(file))
        end}]

    }.

When I run the tests I get following error (only part of the error pasted below for brevity). Looking at the error below, I am kind of feeling that file module is not being mocked (or the mock of file module being overridden when I set the other mock using meck:new(lhttpc). What could be going wrong here?

=ERROR REPORT==== 16-Feb-2013::20:17:24 ===
** Generic server file_meck terminating 
** Last message in was {'EXIT',<0.110.0>,
                    {compile_forms,
                     {error,
                      [{[],
                        [{none,compile,
                          {crash,beam_asm,
                           {undef,
                            [{file,get_cwd,[],[]},
                             {filename,absname,1,
                              [{file,"filename.erl"},{line,67}]},
                             {compile,beam_asm,1,
                              [{file,"compile.erl"},{line,1245}]},
                             {compile,'-internal_comp/4-anonymous-1-',2,
                              [{file,"compile.erl"},{line,273}]},
                             {compile,fold_comp,3,
                              [{file,"compile.erl"},{line,291}]},
                             {compile,internal_comp,4,
                              [{file,"compile.erl"},{line,275}]},
                             {compile,'-do_compile/2-anonymous-0-',2,
                              [{file,"compile.erl"},{line,152}]}]}}}]}],
                      [{"src/lhttpc_types.hrl",
                        [{31,erl_lint,{new_builtin_type,{boolean,0}}},
                         {31,erl_lint,{renamed_type,bool,boolean}}]}]}}}
Was it helpful?

Solution

This is a catch 22 in Meck, caused by the fact that Meck uses the Erlang compiler, which in turns uses the file module. When Meck tries recompile the file module it needs the file module (through the compiler) and thus crashes.

As of this moment, Meck doesn't handle mocking the file module. Your best alternative is to wrap the file module calls in another module and mock this module instead.

(It is theoretically possible to fix this in Meck by using the internals of the compiler and the code server instead, for example erlang:load_module/2, however this is quite tricky and needs to be designed and tested well)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top