Mocking only one function of a module

Say I have a module called MyModule with the following functions:

  def get_rank(member_id) do
    Account.get_user_by_id(member_id).rank
  end

  def get_rank_2(member_id) do
    get_rank(member_id) * 2
  end

I want to mock the get_rank function so it just returns 0.
So I setup my test as follows:

  setup_with_mocks([
    {MyModule , [:passthrough], [get_rank: fn(member_id) -> 0 end]},
  ]) do

    :ok
  end

If create these two tests, only one of them passes:

  test "mock set up 1" do
    assert 0 == MyModule.get_rank("test")
   end

  test "mock set up 2" do
     assert 0 == MyModule.get_rank_2("test")
  end

This means that get_rank_2 is using the real get_rank rather than the mock. Is there a way to get it to use the mock?

Here:

  setup_with_mocks([
    {MyModule , [:passthrough], [get_rank: fn(member_id) -> 0 end]},
    {MyModule , [:passthrough], [get_rank_2: fn(member_id) -> 0 end]},
  ]) do
    :ok
  end

Or you can just use with_mock in each individual test which I think is the much better idea:

  test "mock set up 1" do
    with_mock MyModule , [:passthrough], [get_rank: fn(member_id) -> 0 end] do
      assert 0 == MyModule.get_rank("test")
    end
  end

And you can do the same with get_rank_2.

Thinking of it though, I am not sure what does :passthrough even do. Why would it “pass through arguments to the original module” if you are mocking it and the original module’s function will never be hit? :thinking:

Strange.

Mocking a call without the module specifier (like get_rank in get_rank_2) is explicitly NOT supported by Mock:

It is important to understand that only fully qualified function calls get mocked. The reason for this is because of the way Meck is structured. Meck creates a thin wrapper module with the name of the mocked module (and passes through any calls to the original Module in case passthrough is used). The original module is renamed, but otherwise unmodified. Once the call enters the original module, the local function call jumps stay in the module.

1 Like

I still don’t get the idea of passthrough; why would you call the original module at all? You’re mocking it add shouldn’t be interested in it after all, no?

I’ve only used Mock a little bit quite a while ago, but my interpretation of the docs for :passthrough is that it’s intended for situations where SomeModule defines multiple functions but only one should be replaced with a mock.

For instance, it would be useful if you wanted to mock only DateTime.now without also defining mocks for every call to Datetime.to_string etc.

1 Like

While that makes sense I still can’t quite imagine it with concrete code. Would you give an example?