Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not working, at least not in the way I expected #71

Closed
naps62 opened this issue Sep 13, 2017 · 8 comments
Closed

Not working, at least not in the way I expected #71

naps62 opened this issue Sep 13, 2017 · 8 comments

Comments

@naps62
Copy link

naps62 commented Sep 13, 2017

I'm finding that this package does not work when mocking functions that are called indirectly. Here's the example I came up with to reproduce:

defmodule MyApp.MyMod do
  def value do
    1
  end

  def indirect_value do
    value()
  end
end

and here are some assertions I tried to make for it:

defmodule MyApp.MyModTest do
  use ExUnit.Case, async: false
  import Mock

  alias MyApp.MyMod

  test "test" do
    # these two work, obviously
    assert MyMod.value == 1
    assert MyMod.indirect_value == 1

    # in this block, the second assertion fails with:
    # (UndefinedFunctionError) function MyApp.MyMod.indirect_value/0 is undefined (module MyApp.MyMod is not available)
    # I guess this is to be expected, although unintuitive
    with_mock MyMod, [value: fn -> 2 end] do
      assert MyMod.value == 2
      assert MyMod.indirect_value == 2
    end

    # and in this block (adding :passthrough), the second assertion fails, because the indirect_value function still returns 1
    with_mock MyMod, [:passthrough], [value: fn -> 2 end] do
      assert MyMod.value == 2
      assert MyMod.indirect_value == 2
    end
  end
end

I'm not sure how I should use the package to test this kind of calls, if that's at all possible

@Olshansk
Copy link
Collaborator

What you're describing is definitely not the behavior I'd expect either. Unfortunately, I don't have a good solution to this right now...

According to http:https://elixir-lang.readthedocs.io/en/latest/technical/scoping.html:

Any unbound identifier is treated as a local function call

When we're using the :passthrough flag and call indirect_value, we enter the scope of MyMod and execute the locally scope function since we're not referencing it via the module name. The only workarounds I have to this at the moment are:

  1. Replacing value() with __MODULE__.value()
  2. Mocking indirect_value in your test
  3. Using dependency injection and passing:

Implementation would look like this:

  def indirect_value(value) do
    value.()
  end

Test will look like this:

  assert MyMod.indirect_value(&MyMod.value/0) == 2

All of this being said, I'm going to keep thinking about this for a little while longer to see if we could get the expected behavior to function properly.

@afjackman
Copy link

afjackman commented Sep 19, 2017

I'm having the same exact problem! Would love to see a solution.

@keown
Copy link

keown commented Feb 23, 2018

same issue here

@Olshansk
Copy link
Collaborator

I've been looking for a solution for this on and off.

The only potential path I've found so far is manually updating the beam file after compilation. There are still a couple hooks I need to jump through to get it working but I'll keep this thread updated.

@acarvajal-gap
Copy link

+1

@messutied
Copy link

+1

1 similar comment
@dxnxk-0
Copy link

dxnxk-0 commented Jan 27, 2023

+1

@gaffneylg
Copy link

Having the same issue here, has there been any solutions or workarounds found?

@naps62 naps62 closed this as completed Nov 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants