Code Monkey home page Code Monkey logo

mock's Introduction

Build Status

Mock

A mocking library for the Elixir language.

We use the Erlang meck library to provide module mocking functionality for Elixir. It uses macros in Elixir to expose the functionality in a convenient manner for integrating in Elixir tests.

See the full reference documentation.

Table of Contents

Installation

First, add mock to your mix.exs dependencies:

def deps do
  [{:mock, "~> 0.3.0", only: :test}]
end

and run $ mix deps.get.

with_mock - Mocking a single module

The Mock library provides the with_mock macro for running tests with mocks.

For a simple example, if you wanted to test some code which calls HTTPotion.get to get a webpage but without actually fetching the webpage you could do something like this:

defmodule MyTest do
  use ExUnit.Case, async: false

  import Mock

  test "test_name" do
    with_mock HTTPotion, [get: fn(_url) -> "<html></html>" end] do
      assert "<html></html>" == HTTPotion.get("http://example.com")
    end
  end
end

The with_mock macro creates a mock module. The keyword list provides a set of mock implementation for functions we want to provide in the mock (in this case just get). Inside with_mock we exercise the test code and we can check that the call was made as we expected using called and providing the example of the call we expected.

with_mocks - Mocking multiple modules

You can mock up multiple modules with with_mocks.

defmodule MyTest do
  use ExUnit.Case, async: false

  import Mock

  test "multiple mocks" do
    with_mocks([
      {Map,
       [],
       [get: fn(%{}, "http://example.com") -> "<html></html>" end]},
      {String,
       [],
       [reverse: fn(x) -> 2*x end,
        length: fn(_x) -> :ok end]}
    ]) do
      assert Map.get(%{}, "http://example.com") == "<html></html>"
      assert String.reverse(3) == 6
      assert String.length(3) == :ok
    end
  end
end

The second parameter of each tuple is opts - a list of optional arguments passed to meck.

test_with_mock - with_mock helper

An additional convenience macro test_with_mock is supplied which internally delegates to with_mock. Allowing the above test to be written as follows:

defmodule MyTest do
  use ExUnit.Case, async: false

  import Mock

  test_with_mock "test_name", HTTPotion,
    [get: fn(_url) -> "<html></html>" end] do
    HTTPotion.get("http://example.com")
    assert_called HTTPotion.get("http://example.com")
  end
end

The test_with_mock macro can also be passed a context argument allowing the sharing of information between callbacks and the test

defmodule MyTest do
  use ExUnit.Case, async: false

  import Mock

  setup do
    doc = "<html></html>"
    {:ok, doc: doc}
  end

  test_with_mock "test_with_mock with context", %{doc: doc}, HTTPotion, [],
    [get: fn(_url, _headers) -> doc end] do

    HTTPotion.get("http://example.com", [foo: :bar])
    assert_called HTTPotion.get("http://example.com", :_)
  end
end

setup_with_mocks - Configure all tests to have the same mocks

The setup_with_mocks mocks up multiple modules prior to every single test along while calling the provided setup block. It is simply an integration of the with_mocks macro available in this module along with the setup macro defined in elixir's ExUnit.

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

  setup_with_mocks([
    {Map, [], [get: fn(%{}, "http://example.com") -> "<html></html>" end]}
  ]) do
    foo = "bar"
    {:ok, foo: foo}
  end

  test "setup_with_mocks" do
    assert Map.get(%{}, "http://example.com") == "<html></html>"
  end
end

The behaviour of a mocked module within the setup call can be overridden using any of the methods above in the scope of a specific test. Providing this functionality by setup_all is more difficult, and as such, setup_all_with_mocks is not currently supported.

Currently, mocking modules cannot be done asynchronously, so make sure that you are not using async: true in any module where you are testing.

Also, because of the way mock overrides the module, it must be defined in a separate file from the test file.

Mocking input dependent output

If you have a function that should return different values depending on what the input is, you can do as follows:

defmodule MyTest do
  use ExUnit.Case, async: false

  import Mock

  test "mock functions with multiple returns" do
    with_mock(Map, [
      get: fn
        (%{}, "http://example.com") -> "<html>Hello from example.com</html>"
        (%{}, "http://example.org") -> "<html>example.org says hi</html>"
        (%{}, url) -> conditionally_mocked(url)
      end
    ]) do
      assert Map.get(%{}, "http://example.com") == "<html>Hello from example.com</html>"
      assert Map.get(%{}, "http://example.org") == "<html>example.org says hi</html>"
      assert Map.get(%{}, "http://example.xyz") == "<html>Hello from example.xyz</html>"
      assert Map.get(%{}, "http://example.tech") == "<html>example.tech says hi</html>"
    end
  end

  def conditionally_mocked(url) do
    cond do
      String.contains?(url, ".xyz") -> "<html>Hello from example.xyz</html>"
      String.contains?(url, ".tech") -> "<html>example.tech says hi</html>"
    end
  end
end

Mocking functions with different arities

You can mock functions in the same module with different arity:

defmodule MyTest do
  use ExUnit.Case, async: false

  import Mock

  test "mock functions with different arity" do
    with_mock String,
      [slice: fn(string, range)      -> string end,
       slice: fn(string, range, len) -> string end]
    do
      assert String.slice("test", 1..3) == "test"
      assert String.slice("test", 1, 3) == "test"
    end
  end
end

Mock repeated calls

You can mock repeated calls to the same function and arguments to return different results in a series using the in_series call with static values. This does not currently support functions.

Caution: This is only useful in rare instances where the underlying business logic is likely to be stateful. If you can avoid it by using different function arguments, or refactor the function to be stateful, consider that approach first.

defmodule MyTest do
  use ExUnit.Case, async: false

  import Mock

  test "mock repeated calls with in_series" do
    with_mock String,
      [slice: [in_series(["test", 1..3], ["string1", "string2", "string3"])]]
    do
      assert String.slice("test", 1..3) == "string1"
      assert String.slice("test", 1..3) == "string2"
      assert String.slice("test", 1..3) == "string3"
    end
  end
end

passthrough - partial mocking of a module

By default, only the functions being mocked can be accessed from within the test. Trying to call a non-mocked function from a mocked Module will result in an error. This can be circumvented by passing the :passthrough option like so:

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

  test_with_mock "test_name", IO, [:passthrough], [] do
    IO.puts "hello"
    assert_called IO.puts "hello"
  end
end

Assert called - assert a specific function was called

You can check whether or not your mocked module was called.

Assert called - specific value

It is possible to assert that the mocked module was called with a specific input.

defmodule MyTest do
  use ExUnit.Case, async: false

  import Mock

  test "test_name" do
    with_mock HTTPotion, [get: fn(_url) -> "<html></html>" end] do
      HTTPotion.get("http://example.com")
      assert_called HTTPotion.get("http://example.com")
    end
  end
end

Assert called - wildcard

It is also possible to assert that the mocked module was called with any value by passing the :_ wildcard.

defmodule MyTest do
  use ExUnit.Case, async: false

  import Mock

  test "test_name" do
    with_mock HTTPotion, [get: fn(_url) -> "<html></html>" end] do
      HTTPotion.get("http://example.com")
      assert_called HTTPotion.get(:_)
    end
  end
end

Assert called - pattern matching

assert_called will check argument equality using == semantics, not pattern matching. For structs, you must provide every property present on the argument as it was called or it will fail. To use pattern matching (useful when you only care about a few properties on the argument or need to perform advanced matching like regex matching), provide custom argument matcher(s) using :meck.is/1.

defmodule User do
  defstruct [:id, :name, :email]
end

defmodule Network do
  def update(%User{} = user), do: # ...
end

defmodule MyTest do
  use ExUnit.Case, async: false

  import Mock

  test "test_name" do
    with_mock Network, [update: fn(_user) -> :ok end] do
      user = %User{id: 1, name: "Jane Doe", email: "[email protected]"}
      Network.update(user)

      assert_called Network.update(
        :meck.is(fn user ->
          assert user.__struct__ == User
          assert user.id == 1

          # matcher must return true when the match succeeds
          true
        end)
      )
    end
  end
end

Assert not called - assert a specific function was not called

assert_not_called will assert that a mocked function was not called.

defmodule MyTest do
  use ExUnit.Case, async: false

  import Mock

  test "test_name" do
    with_mock HTTPotion, [get: fn(_url) -> "<html></html>" end] do
      # Using Wildcard
      assert_not_called HTTPotion.get(:_)

      HTTPotion.get("http://example.com")

      # Using Specific Value
      assert_not_called HTTPotion.get("http://another-example.com")
    end
  end
end

Assert called exactly - assert a specific function was called exactly x times

assert_called_exactly will assert that a mocked function was called exactly the expected number of times.

defmodule MyTest do
  use ExUnit.Case, async: false

  import Mock

  test "test_name" do
    with_mock HTTPotion, [get: fn(_url) -> "<html></html>" end] do
      HTTPotion.get("http://example.com")
      HTTPotion.get("http://example.com")

      # Using Wildcard
      assert_called_exactly HTTPotion.get(:_), 2

      # Using Specific Value
      assert_called_exactly HTTPotion.get("http://example.com"), 2
    end
  end
end

Assert called at least - assert a specific function was called at least x times

assert_called_at_least will assert that a mocked function was called at least the expected number of times.

defmodule MyTest do
  use ExUnit.Case, async: false

  import Mock

  test "test_name" do
    with_mock HTTPotion, [get: fn(_url) -> "<html></html>" end] do
      HTTPotion.get("http://example.com")
      HTTPotion.get("http://example.com")
      HTTPotion.get("http://example.com")

      # Using Wildcard
      assert_called_at_least HTTPotion.get(:_), 2

      # Using Specific Value
      assert_called_at_least HTTPotion.get("http://example.com"), 2
    end
  end
end

Assert call order

call_history will return the meck.history(Module) allowing you assert on the order of the function invocation:

defmodule MyTest do
  use ExUnit.Case, async: false

  import Mock

  test "test_name" do
    with_mock HTTPotion, [get: fn(_url) -> "<html></html>" end] do
      HTTPotion.get("http://example.com")

      assert call_history(HTTPotion) ==
        [
          {pid, {HTTPotion, :get, ["http://example.com"]}, "<html></html>"}
        ]
    end
  end
end

You can use any valid Elixir pattern matching/multiple function heads to accomplish this more succinctly, but remember that the matcher will be executed for all function calls, so be sure to include a fallback case that returns false. For mocked functions with multiple arguments, you must include a matcher/pattern for each argument.

defmodule Network.V2 do
  def update(%User{} = user, changes), do: # ...

  def update(id, changes) when is_integer(id), do: # ...

  def update(_, _), do: # ...
end

defmodule MyTest do
  use ExUnit.Case, async: false

  import Mock

  test "test_name" do
    with_mock Network.V2, [update: fn(_user, _changes) -> :ok end] do
      Network.V2.update(%User{id: 456, name: "Jane Doe"}, %{name: "John Doe"})
      Network.V2.update(123, %{name: "John Doe", email: "[email protected]"})
      Network.V2.update(nil, %{})

      # assert that `update` was called with user id 456
      assert_called Network.V2.update(
        :meck.is(fn
          %User{id: 456} -> true
          _ -> false
        end),
        :_
      )

      # assert that `update` was called with an email change
      assert_called Network.V2.update(
        :_,
        :meck.is(fn
          %{email: "[email protected]"} -> true
          _ -> false
        end)
      )
    end
  end
end

NOT SUPPORTED

Mocking internal function calls

A common issue a lot of developers run into is Mock's lack of support for mocking internal functions. Mock will behave as follows:

defmodule MyApp.IndirectMod do

  def value do
    1
  end

  def indirect_value do
    value()
  end

  def indirect_value_2 do
    MyApp.IndirectMod.value()
  end

end
defmodule MyTest do
  use ExUnit.Case, async: false
  import Mock

  test "indirect mock" do
    with_mocks([
      { MyApp.IndirectMod, [:passthrough], [value: fn -> 2 end] },
    ]) do
      # The following assert succeeds
      assert MyApp.IndirectMod.indirect_value_2() == 2
      # The following assert also succeeds
      assert MyApp.IndirectMod.indirect_value() == 1
    end
  end
end

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.

Big thanks to @eproxus (author of Meck) who helped explain this to me. We're looking into some alternatives to help solve this, but it is something to be aware of in the meantime. The issue is being tracked in Issue 71.

In order to workaround this issue, the indirect_value can be rewritten like so:

  def indirect_value do
    __MODULE__.value()
  end

Or, like so:

  def indirect_value do
    MyApp.IndirectMod.value()
  end

Mocking macros

Currently mocking macros is not supported. For example this will not work because Logger.error/1 is a macro:

with_mock Logger, [error: fn(_) -> 42 end] do
   assert Logger.error("msg") == 42
end

This code will give you this error: Erlang error: {:undefined_function, {Logger, :error, 1}}

As a workaround, you may define a wrapper function for the macro you need to invoke:

defmodule MyModule do
  def log_error(arg) do
    Logger.error(arg)
  end
end

Then in your test you can mock that wrapper function:

with_mock MyModule, [log_error: fn(_) -> 42 end] do
   assert MyModule.log_error("msg") == 42
end

Tips

The use of mocking can be somewhat controversial. I personally think that it works well for certain types of tests. Certainly, you should not overuse it. It is best to write as much as possible of your code as pure functions which don't require mocking to test. However, when interacting with the real world (or web services, users etc.) sometimes side-effects are necessary. In these cases, mocking is one useful approach for testing this functionality.

Also, note that Mock has a global effect so if you are using Mocks in multiple tests set async: false so that only one test runs at a time.

Help

Open an issue.

Publishing New Package Versions

For library maintainers, the following is an example of how to publish new versions of the package. Run the following commands assuming you incremented the version in the mix.exs file from 0.3.4 to 0.3.5:

git commit -am "Increase version from 0.3.4 to 0.3.5"
git tag -a v0.3.5 -m "Git tag 0.3.5"
git push origin --tags
mix hex.publish

Suggestions

I'd welcome suggestions for improvements or bugfixes. Just open an issue.

mock's People

Contributors

1st8 avatar achempion avatar amarandon avatar amilkr avatar bartj3 avatar bdionne avatar betree avatar carpodaster avatar connorjacobsen avatar davidrawk-blake avatar duksis avatar era avatar fracek avatar francisco-castro avatar georgetaveras1231 avatar jeremyvdw avatar jjh42 avatar josephwilk avatar julianespinel avatar keqi avatar kianmeng avatar mattfreer avatar mrjoelkemp avatar mtrudel avatar olshansk avatar pragdave avatar speeddragon avatar spscream avatar stjhimy avatar superhawk610 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mock's Issues

with_mocks/3 is undefined

Hi I was trying to use with_mocks/3

with_mocks(HTTPoison, [
      get: fn
        "http://example.com" -> "<html>Hello from example.com</html>"
        "http://example.org" -> "<html>example.org says hi</html>"
      end
    ]) do

like written in the docs but it throws an undefined function error. Any ideas why?

Mock function which comes from use have an error

Hello, i have issue, when i try to mock some functions which comes from use, let's see an example:

defmodule Common do
 defmacro __using__(_opts) do
   quote do
     def run(method) do 
        apply(__MODULE__, String.to_atom(method), [method])
     end 
   end
 end
end

test:

defmodule CommonTest do
  use ExUnit.Case
  import Mock
  defmodule CommonWrapper
    use Common
    def test(_) do
    end
  end

test "some mock method" do
  test_fn = fn(_) -> assert :ok end
  with_mock CommonWrapper, [test: test_fn] do
     CommonWrapper.run("test")
  end
end
end

** (UndefinedFunctionError) function CommonWrapper.run/1 is undefined (module CommonWrapper is not available)

Any ideas?

feature request: assert_not_called

I should be able to assert something was not called.
It will either fail on nothing but in some cases I just want to assert that one method is not called and not have to explicitly call out all of the rest. I'll try to do this sometime soon.

Can't mock correctly my code

I am using mock in my phoenix project to test the interaction between a controller and the repo.

I wrote this test in my controller:

test_with_mock "list all entries on index", %{conn: conn}, Repo, [:passthrough], [] do
  conn = get conn, board_column_path(conn, :index, 1)

  assert called Repo.all from(c in Column, where: c.board_id == 1)
  assert html_response(conn, 200) =~ "Listing columns"
end

And this is the actual code:

def index(conn, %{"board_id" => board_id}) do
  columns = Repo.all from(c in Column, where: c.board_id == ^board_id)
  render(conn, "index.html", columns: columns)
end

The output is the following:

1) test list all entries on index (SimpleBoard.ColumnControllerTest)
test/controllers/column_controller_test.exs:17
Expected truthy, got false
code: called(Repo.all(from(c in Column, where: c.board_id() == 1)))
stacktrace:
  test/controllers/column_controller_test.exs:20

Can you help me to understand where is the problem?
Thanks!

Stronger refute statements

Currently if I want to say that a method wasn't called I need to provide all of the arguments like:

refute called Module.function(:green, :purple)

Ideally I'd like to be able to have a test which says that the method hasn't been called at all with any set of arguments. I tried this:

refute called Module.function(_, _)

But I get unbound variable _.

I'm still learning Elixir, so any help would be appreciated!

Problem with fetch package mock on Windows

When I try to call

mix deps.get

I can't install dependency and I see an error:

Dependency resolution completed:                                    
  meck 0.8.10                                                       
  mock 0.3.1                                                        
* Updating mock (Hex package)                                       
  Checking package (https://repo.hex.pm/tarballs/mock-0.3.1.tar)    
  Fetched package                                                   
** (exit) an exception was raised:                                  
    ** (CaseClauseError) no case clause matching: {:error, :einval} 
        (stdlib) erl_tar.erl:598: :erl_tar.extract1/4               
        (stdlib) erl_tar.erl:571: :erl_tar.foldl_read1/4            
        (stdlib) erl_tar.erl:554: :erl_tar.foldl_read0/4            
        (stdlib) erl_tar.erl:546: :erl_tar.foldl_read/4             
        (hex) lib/hex/tar.ex:94: Hex.Tar.extract_contents/3         
        (hex) lib/hex/tar.ex:43: Hex.Tar.unpack/3                   
        (hex) lib/hex/scm.ex:110: Hex.SCM.checkout/1                
        (mix) lib/mix/dep/fetcher.ex:61: Mix.Dep.Fetcher.do_fetch/3 
    (stdlib) erl_tar.erl:556: :erl_tar.foldl_read0/4                
    (stdlib) erl_tar.erl:546: :erl_tar.foldl_read/4                 
    (hex) lib/hex/tar.ex:94: Hex.Tar.extract_contents/3             
    (hex) lib/hex/tar.ex:43: Hex.Tar.unpack/3                       
    (hex) lib/hex/scm.ex:110: Hex.SCM.checkout/1                    
    (mix) lib/mix/dep/fetcher.ex:61: Mix.Dep.Fetcher.do_fetch/3     
    (mix) lib/mix/dep/converger.ex:177: Mix.Dep.Converger.all/9     
    (mix) lib/mix/dep/converger.ex:186: Mix.Dep.Converger.all/9```

Mock raises random errors with OTP 20

I use mock 0.2.1 in my ExUnit test suite. Tests pass with Elixir 1.4 and OTP 18 and 19, but I get errors with OTP 20. The errors happen in some, but not all, tests that create mocks.

     ** (ErlangError) erlang error: {:compile_forms, {:error, [{[], [{:none, :compile, {:crash, :lint_module, {:badarg, [{:erlang, :atom_to_list, [[GenServer]], []}, {:erl_lint, :is_latin1_name, 1, [file: 'erl_lint.erl', line: 3055]}, {:erl_lint, :check_module_name, 3, [file: 'erl_lint.erl', line: 3048]}, {:erl_lint, :behaviour_callbacks, 3, [file: 'erl_lint.erl', line: 973]}, {:erl_lint, :all_behaviour_callbacks, 3, [file: 'erl_lint.erl', line: 934]}, {:erl_lint, :behaviour_check, 2, [file: 'erl_lint.erl', line: 921]}, {:erl_lint, :post_traversal_check, 2, [file: 'erl_lint.erl', line: 893]}, {:erl_lint, :module, 3, [file: 'erl_lint.erl', line: 536]}, {:compile, :lint_module, 2, [file: 'compile.erl', line: 1109]}, {:compile, :"-internal_comp/5-anonymous-1-", 3, [file: 'compile.erl', line: 342]}, {:compile, :fold_comp, 4, [file: 'compile.erl', line: 369]}, {:compile, :internal_comp, 5, [file: 'compile.erl', line: 353]}, {:compile, :"-do_compile/2-anonymous-0-", 2, [file: 'compile.erl', line: 177]}, {:compile, :"-do_compile/2-anonymous-1-", 1, [file: 'compile.erl', line: 190]}]}}}]}], []}}
     stacktrace:
       (meck) src/meck_proc.erl:95: :meck_proc.start(MyMocked.ModuleFoo, [:passthrough])
       test/my_mocked/module_foo_test.exs:395: anonymous fn/2 in MyMocked.ModuleFooTest.test the test description I typed for this specific test case/1
       (elixir) lib/enum.ex:1755: Enum."-reduce/3-lists^foldl/2-0-"/3
       test/my_mocked/module_foo_test.exs:395: (test)

It looks like that sometimes the mocks are not cleared and "leak" in other tests, judging from this other kind of errors that happens randomly:

     ** (UndefinedFunctionError) function Foo.Bar.baz/0 is undefined or private. Did you mean one of:

           * baz/0

doctest

Is it possible stub doctest? I need stup HTTPpoison inside doctests

Feature Request: Capture arguments

Sometimes I need to capture a mocked function's arguments and then compare them to the nth time that that function was called. It was able to accomplish this by dropping down to :meck but it would be nice if there were a helper macro / function

Workaround Example

# something called MyMockedModule.myfunc 2 times 
# and I want to make sure that the params are the same both times
[{_, {_, :myfunc, args0}, _}, {_, {_, :myfunc, args1}, _}] =
        unquote(MyMockedModule)
        |> :meck.history()

assert Map.equal?(args0, args1)

Assert a function was called without caring about arguments?

I'm trying to write a test where I'm asserting that the Task.async/1 function was called, but I don't care about what arguments it was called with. Here's a simple example:

test "calls Task.async/1" do
  with_mock Task, [async: fn(_) -> end] do
    Task.async(fn -> IO.puts("test") end)
    assert called Task.async
  end
end

But this test always returns Expected truthy, got false. Is there a way to assert that a function was called regardless of the arguments? I tried doing assert called Task.async(_), but that raises a CompileError.

Alternatively, how can I specify that Task.async was called with an anonymous function? I've tried a few things, but haven't been able to get the test to pass. Thanks in advance!

Error raised when matching IO.gets

I am writing a test and need to mock IO.gets. The function I am testing is not written yet, so the test is expected to fail. However, it is currently raising an ugly error instead. This is the error text:

16:20:09.868 [error] GenEvent handler ExUnit.CLIFormatter installed in #PID<0.152.0> terminating
** (UndefinedFunctionError) function IO.chardata_to_string/1 is undefined (module IO is not available)
    (elixir) IO.chardata_to_string('/Users/dana/Development/gherkin-elixir-sandbox')
    (elixir) lib/path.ex:313: Path.relative_to_cwd/1
    (ex_unit) lib/ex_unit/formatter.ex:292: ExUnit.Formatter.with_location/1
    (ex_unit) lib/ex_unit/formatter.ex:112: ExUnit.Formatter.format_test_failure/5
    (ex_unit) lib/ex_unit/cli_formatter.ex:71: ExUnit.CLIFormatter.handle_event/2
    (stdlib) gen_event.erl:533: :gen_event.server_update/4
    (stdlib) gen_event.erl:515: :gen_event.server_notify/4
    (stdlib) gen_event.erl:256: :gen_event.handle_msg/5
    (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Last message: {:test_finished, %ExUnit.Test{case: GherkinTokenScannerTest, logs: "", name: :"test .read\\2 returns a token when line can be read from IO", state: {:failed, [{:error, %ErlangError{original: {:undefined_function, {IO, :gets, 0}}}, [{:meck, :check_expect_result, 1, [file: 'src/meck.erl', line: 670]}, {Mock, :_install_mock, 2, [file: 'lib/mock.ex', line: 127]}, {GherkinTokenScannerTest, :"test .read\\2 returns a token when line can be read from IO", 1, [file: 'test/token_scanner_test.exs', line: 17]}]}]}, tags: %{async: false, case: GherkinTokenScannerTest, describe: nil, file: "/Users/dana/Development/gherkin-elixir-sandbox/test/token_scanner_test.exs", line: 11, registered: %{}, test: :"test .read\\2 returns a token when line can be read from IO", type: :test}, time: 68324}}
State: %{colors: [enabled: true], failures_counter: 0, invalids_counter: 0, seed: 180995, skipped_counter: 0, tests_counter: %{test: 43}, trace: false, width: 80}

My test file is as follows:

defmodule GherkinTokenScannerTest do
  use ExUnit.Case, async: false
  doctest Gherkin.TokenScanner

  import Mock

  test ".read\\2 returns a token when line can be read from IO" do
    expected_output = %Gherkin.Token{
      line: %Gherkin.GherkinLine{text: "Feature: Hello world", line_number: 2},
      location: %{line: 2}
    }

    with_mock IO, [gets: fn() -> "Feature: Hello world" end] do
      assert Gherkin.TokenScanner.read(:stdin, 1) == expected_output
    end
  end
end

And my module code looks like this:

defmodule Gherkin.TokenScanner do
  def read(io, line_number \\ 0) do
  end
end

This is probably just me screwing something up, but if you could point me in the right direction, that would be much appreciated.

Thanks!

Not working, at least not in the way I expected

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

Execute some code when the mock is called

I have found quite handy sometimes to have the possibility to execute some code when the mock is called. One good use case is raising an exception when the mock is called.
In RhinoMocks there is something like this:

myStub
    .Stub(_ => _.Create(Arg<Invoice>.Is.Anything))
    .WhenCalled(_ => 
    {
        throw new Exception
    );

Is that possible with mock?
Thanks by the way for the nice library

multiple mock

i trying mock some function in my module. I have many functions but need mock only some. I thy this

with_mock(Api.PageParser, [
      load_html: fn(url) -> PageParserMock.load_html(url) end,
      load_user: fn(user_id, api_key) -> PageParserMock.load_user(user_id, api_key) end,
      load_listings: fn(user_id, api_key) -> PageParserMock.load_listings(user_id, api_key) end
    ])

but when i run test it return

** (UndefinedFunctionError) function Api.PageParser.validate/1 is undefined or private. Did you mean one of:
     
           * validate/1

when i try

with_mocks([
      {Api.PageParser, [], [load_html: fn(url) -> PageParserMock.load_html(url) end]},
      {Api.PageParser, [], [load_user: fn(user_id, api_key) -> PageParserMock.load_user(user_id, api_key) end]},
      {Api.PageParser, [], [load_listings: fn(user_id, api_key) -> PageParserMock.load_listings(user_id, api_key) end]},
    ]) do

i got

** (UndefinedFunctionError) function Api.PageParser.validate/1 is undefined (module Api.PageParser is not available)

Why i have this behauver?

Failure when mocking exceptions

When mocking exceptions like this:

    defmodule MyModule do
      def foo, do: IO.puts "Hello world"
    end

    with_mock MyModule, [foo: fn() -> raise "ohnoz!" end] do
      try do
        MyModule.foo
      rescue
        err ->
          IO.puts "Caught error #{inspect err}"
      end
      assert called MyModule.foo
    end

I get the following failure:

     Assertion with == failed
     code: :meck.validate(MyModule) == true
     lhs:  false
     rhs:  true

Why do we call :meck.validate() after the test ? That seems to be causing the trouble.

Fails to mock function called from function in same module

Expected Behavior

with_mocks successfully mocks a function in a module (separate from the testing module) that is called from a function in the same module.

Current Behavior

The function is not mocked

Steps to Reproduce

git clone https://github.com/LLay/mock_test.git
mix deps.get
mix test

Result should be:

$ mix test
.

  1) test bar is mocked when called from own module (MockerTest)
     test/mocker_test.exs:12
     Assertion with == failed
     code:  assert Mocker.foo() == :mocked
     left:  :bar
     right: :mocked
     stacktrace:
       test/mocker_test.exs:14: (test)



Finished in 0.06 seconds
2 tests, 1 failure

Context (Environment)

Demo (from above): https://github.com/LLay/mock_test

MacBook-Pro:mocker user1$  asdf current
elixir         1.6.5 
erlang         20.2.3 

Conversation

I'm guessing this behavior is occurring because the caller function (foo in my example) is compiled before the mocked version of function in question (bar) is. Is there any way around this?

Note that this is not an issue with :passthrough. Removing this option only leads to the caller (foo) being undefined

Stubbing out methods from use-d code...

I want to stub out a function that is in a module use-d by another module, like so:

defmodule A do
  defmacro __using__(_opts) do
    quote do
      def avoid_during_test(foo) do
        {:error, "don't ever want to get here during test"}
      end
    end
  end
end

defmodule B do
  use A
  def init() do
    avoid_during_test(:ignored_in_test_anyway)
    exec_during_test(:whatever)
  end
end

In the test script:

defmodule BTest do
  use ExUnit.Case
  import Mock
  test "my test" do
    with_mocks([
      {B, [:passthrough], [avoid_during_test: fn(_arg) -> :ok end]}
    ]) do
       B.init()
       assert(true)
    end
  end
end

Attempting to run mix test results in the following bundle of joy...

** (ErlangError) Erlang error: {:undefined_function, {B, :avoid_during_test, 1}}
       (meck) <path to project>/deps/meck/src/meck.erl:712: :meck.check_expect_result/1
       (mock) lib/mock.ex:150: Mock._install_mock/2
       (elixir_lib) test/util/b_test.exs:11: anonymous fn/2 in BTest."my test"/1
       (elixir) lib/enum.ex:1899: Enum."-reduce/3-lists^foldl/2-0-"/3
       test/util/b_test.exs:5: (test)

In iex -S mix, if I can run B.__info__(:functions) and, lo and behold, I get:

[
  ...
  avoid_during_test: 1,
  exec_during_test: 1,
  ...
]

This is not the first time I have encountered this problem, but it's the first time I couldn't find a reasonable work-around I am comfortable with. If it is recognized as essentially a function native to B by the module, why is it not recognized as such by Mock? The arity is correct and everything...

In real life, this issue arose when I was attempting to unit test a module (substituted with B in the example) that uses another module (A) to set up an AMQP endpoint (publisher or subscriber) based on a config buffer (the args). I really don't want to stub out the multiple AMQP and Process.monitor calls in that use-d module's (A's) function, when I just want to assume a valid connection is established.

Being a neurotic, I assume I must be doing something wrong, but am willing to accept that this might be a bug in how Mock works.

Erlang version: 19.2
Elixir version: 1.6.5
Mock version: 0.2.0

Thanks for your time and attention.
Jeff Osborn, universal threat to the gene pool.

ExUnit runner gets killed when mock is used inside an umbrella project AND processes

I have an umbrella project which contains a subproject that uses Mock and Agent to mock out File functions. The same code works fine in a project that is not part of an umbrella project.

But when I run mix test for the umbrella project, the ExUnit runner gets killed with an output like this:
** (EXIT from #PID<0.92.0>) killed.

I have created a repository to reproduce this behaviour here: https://github.com/XelaRellum/mock_bug_with_umbrella

Thanks a lot for you help and for creating Mock!

meck_proc already started

I'm mocking the same module in two different test files.

If I set async: true in both of these test files, I get the following error:

  1) test <module_name> fails when <test_name>
     <file_name>:<line_num>
     ** (ErlangError) erlang error: {:already_started, #PID<0.886.0>}
     stacktrace:
       src/meck_proc.erl:95: :meck_proc.start(<mocked_module_name>, [])
       <file_name>:<line_num>: anonymous fn/2 in <test_file> <describe> fails <test_name>
       (elixir) lib/enum.ex:1623: Enum."-reduce/3-lists^foldl/2-0-"/3
       <file_name>:<line_num>: (test)

The workaround is to set async: false in at least one of the two files where the same module is being mocked.

Without investigating how :meck is implemented, I think I have a good guess as to why this is happening and understand that the root cause of the issue is the interdependence of my unit tests; I really shouldn't be mocking the same module in two different places.

Without refactoring my tests, do you have a suggestion for a workaround to have them run asynchronously?

Problems with mocking a request with timeout

Hello.

I'm mocking two get requests like this:

test_with_mock "my test", HTTPotion, [],
  [
    get: fn("http://my-url-one") ->
      %{body: "some get stuff", status_code: 200}
    end,
    get: fn("http://my-url-two") ->
      %{body: "{\"id\": 1}", status_code: 200}
    end
  ] do

  # ...

It works and everything is ok. But once I add a timeout param to the actual http://my-url-one request in the code:

HTTPotion.get("http://my-url-one", [timeout: timeout])

And to the mock as well:

get: fn("http://my-url-one", [timeout: timeout]) ->
  %{body: "some get stuff", status_code: 200}
end

The test gets broken with the following error:

** (FunctionClauseError) no function clause matching in anonymous fn/2

Any clue how to properly mock a request with timeout param?

Thanks!

Module not available in mock do block

I have a custom module MyApp.MyModule with a function do_something in a compiled file (my_module.ex).

defmodule MyApp.MyModule do
  def do_something(the_something), do: "Doing #{the_something}"
end

And I have a test that is trying to mock the function and see that it was called, but I'm getting an error saying that the module is not available.

defmodule MyApp.MyModuleTest do
  import Mock

  alias MyApp.MyModule

  describe "see if I can mock" do
    test "Try mocking" do
      assert MyModule.do_something("a thing") == "Doing a thing")
      with_mock(MyModule, [do_something: fn(_the_something) -> "Something else" end]) do
        assert MyModule.do_something("another thing") == "Something else"
      end
    end
  end
end

And the error is:

** (UndefinedFunctionError) function MyApp.MyModule.do_something/1 is undefined (module MyApp.MyModule is not available)
     code: MyApp.MyModule.do_something("another thing")
     stacktrace:
       (my_app) MyApp.MyModule.do_something("another thing")
       test/my_app/my_module_tests.exs:10: (test)

As you can see, I can call the function perfectly fine outside of the mock, but if I can't call it inside the mock, it defeats the entire purpose of the mocking tool.

Syntax sugar for multiple mocks?

I do not like that I need a lot of with_mock blocks when I need multiple mocks for a test (it happens in some projects). I would love to see a better solution for this case, but I have no idea on how to improve it. What you say?

defdelegate problem

Mocks are not working for functions defined with defdelegate expressions:

    pid = self()
    with_mock Process, [:passthrough], alive?: &(&1 != pid) do
      Process.alive?(pid)
      # ==> true (expected false)
    end

Mock is hard to use in test helper libraries

Steps to reproduce:

  1. Download + unzip https://files.emanuel.industries/mocktest.tar.gz (+ mix deps.get + mix test)
  2. Comment out L4 of test/support/mocktest/helpers.ex (as a gist, https://gist.github.com/22b068e180a947889c08b4f399e381dc)
  3. Run mix test

Expected behavior

Compilation succeeds + tests pass.

Actual behavior

Compilation fails because with_mock has a hidden dependency on assert.

This is annoying because it's often convenient to mock external API calls (for instance) for a bunch of different tests, and a test helper library is often a good place to put that. Importing ExUnit.Assertions works as a workaround, but it's pretty ugly and non-obvious.

Can't mock Task

Hey, not sure if I missed something obvious but I can't seem to mock Task.

with_mock Task, [start: fn(_) -> end] do
end

Results in...

14:19:07.262 [error] GenServer #PID<0.233.0> terminating
** (stop) killed
Last message: {:EXIT, #PID<0.220.0>, :killed}
State: {:state, {#PID<0.233.0>, Supervisor.Default}, :simple_one_for_one, [{:child, :undefined, GenServer, {GenServer, :start_link, []}, :temporary, 5000, :worker, [GenServer]}], {:set, {:set, 2, 16, 16, 8, 80, 48, {[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []}, {{[], [#PID<0.241.0>], [], [], [], [], [], [], [], [], [], [#PID<0.235.0>], [], [], [], []}}}}, 3, 5, [], 0, Supervisor.Default, {[%{id: GenServer, restart: :temporary, start: {GenServer, :start_link, []}}], [strategy: :simple_one_for_one]}}

Am I doing something silly?

Question about mocking child function

Let's say I have something like this:

defmodule MyModule do

   def functionA do
      :rand.uniform(10)
   end

   def functionB do
      functionA() |> add
   end

   def add(num) do
      num + 1
   end

end

How can I mock functionA but doing an assert inside the macro, something like this

test "it should return 5 if random number is 4" do
   with_mock MyModule, [functionA: fn() -> 4 end] do
      assert MyModule.functionB() == 5
   end
end

I hope I can explain well my question here, I did a silly example but I need the answer in order to test a wrapper API I'm working on.

Thanks in advance.

passthrough does not work 100% with Elixir

I've been writing my own version of mock for use with my Elixir testing framework Amrita (http://amrita.io/).

I've noticed a bug between Elixir and meck interop. Thought I would also report it here so you are aware of the problem.

I'm still trying to work out if its fixable and how.

To replicate:

git clone -b passthrough_bug [email protected]:josephwilk/mock.git && cd mock && MIX_ENV=test mix deps.get && mix test

The example failing code:

  defmodule Funk do
    def hip? do
      false
    end

    def hop? do
      hip?
    end

    def chicken? do
      false
    end
  end

  test "funk" do
    with_mock Funk, [:passthrough], [hip?: fn() -> true end] do
      Funk.chicken?
      assert Funk.hop?
    end
  end

Fail with error:

  1) test funk (MockTest)
     ** (UndefinedFunctionError) undefined function: MockTest.Funk_meck_original.chicken?/0
     stacktrace:
       MockTest.Funk_meck_original.chicken?()
       src/meck.erl:418: :meck.exec/5
       MockTest.Funk.chicken?()
       test/mock_test.exs:60: MockTest."test funk"/1

trouble replicating examples

Forgive me if I'm accidentally asking a basic Elixir question, but I'm having trouble duplicating the results of the examples.

My test:

defmodule FanPublicApiTest do
  use ExUnit.Case, async: false

  import Mock

  describe "/v2.4/me" do
    test_with_mock 'response is parsable json', HTTPotion,
      [get: fn(_url) -> '{"broken json"' end] do
      response = HTTPotion.get("https://wwwstg.bandsintown.com/v2.4/me", [headers: ["Authorization": Application.fetch_env!(:gateway_regression, :authorization)]])
      IO.puts response.body
    end
  end
end

My results when running tests:

  1) test /v2.4/me response is parsable json (FanPublicApiTest)
     test/integration/fan_public_api_test.exs:7
     ** (UndefinedFunctionError) function HTTPotion.get/2 is undefined (module HTTPotion is not available)
     stacktrace:
       (httpotion) HTTPotion.get("https://wwwstg.bandsintown.com/v2.4/me", [headers: [Authorization: "Token token=**truncated**, auth_method=facebook, auth_login=100000917188511"]])
       test/integration/fan_public_api_test.exs:9: (test)

I've used the HTTPotion.get function successfully without the mocking, am I maybe missing something for my test helper?

Should assertions print what WAS called?

From a usability perspective, I find myself modifying code to printing the expected invocation because what was invoked during the test is not printed to the console.
I feel like any unexpected invocation of the mock should print, or possibly just when verifying assertions.

Is it possible to mock the same function more than once ?

I have a module named Randomise and in it, I have a function integer_random . The function is called multiple times inside another function (the one that is being tested). Let's say that during the test the Randomise.integer_random/1 function is called 6 times, can I mock the same function six times ? And if not, is there a workaround I could use ?

There is any way to improve the `setup_with_mocks` behaviour?

Hi guys, I am trying to clean a bit my tests which in this moment are full of with_mock/4 functions, I found setup_with_mocks that looks like a potential solution, nevertheless, although it helps a lot with some tests that share the same mock, sometimes I need to change the data that was already mocked before in those cases I need to use with_mock/4 inside my test macro.

In order to help explain my issue, I would like to show a little bit of my code:

  describe "A user tries to login" do
    setup_with_mocks([
      {
        MyMockedModule, [], [
          log_user_in: fn(_user) -> {:ok, "Successfully logged in"} end,
          another_mocked_function: fn(_payload) -> standard_user_mock() end,
        ]
      }
    ], %{conn: conn}) do

      {:ok, conn: conn}
    end

    test "test 1", %{conn: conn} do
      conn =
        conn
        |> post(auth_path(conn, :login), %{"email" => "[email protected]", "password" => "1234"})

      body = json_response(conn, 200)
    end

    test "test 2", %{conn: conn} do
      with_mock MyMockedModule, [
        log_user_in: fn(_user) -> %{:error, "message" => "INVALID provided password"} end,
        another_mocked_function: fn(_payload) -> standard_user_mock() end,
      ] do
        conn =
          conn
          |> post(auth_path(conn, :login), %{"email" => "[email protected]", "password" => "1325"})

        body = json_response(conn, 401)
        assert body["errors"]["message"] == "INVALID provided password"
      end
    end
 end

In this example, I use setup_with_mocks to clean the describe group, the issue, as shown above, is when I need to mock a different function of my MyMockedModule the library force me to rewrite all the mocks again (even those that I don't need to change ๐Ÿ™ ) if I don't I will receive a ** (UndefinedFunctionError) function exception.

Maybe am I doing something wrong here, do you have any advice?

Help - having trouble mocking IO.gets

defmodule PlayerTests do
   use ExUnit.Case, async: false

   test "get input from the user" do
      Mock.with_mock IO, [gets: fn(_prompt) -> "1\n" end] do
         result = IO.gets("say something: ")
         assert result == "1\n"
      end 
   end
end

Not sure where I am going wrong with this. When the test is ran and IO.gets("say something: ") is called it doesn't seem to have mocked the function and still prompts the console for input.

Not sure if this is related but I also can't import/require the Mock module. When I do I get a compile error:

** (CompileError) test/player_test.exs:3: module Mock is not loaded and could not be found
(stdlib) lists.erl:1352: :lists.mapfoldl/3
(stdlib) lists.erl:1353: :lists.mapfoldl/3

@version undefined in mix.exs

Shouldn't @version be defined in mix.exs. ?
It's been throwing

mix.exs:12: warning: undefined module attribute @Version, please remove access to @Version or explicitly set it before access

Thanks for the help
paul

assert_called with pattern matching

Imagine that I want to assert that some method got called using an Ecto struct as the argument.
Currently I would do that with (here an example from a Phoenix controller test):

test_with_mock "it works", %{conn: conn, user: user},
  get conn, "/"
  assert_called VisitorLog.track!(user)
end

If the user I pre-built for the test does not have the same associations loaded as the user that will be loaded from the database in the actual controller, then the assertion will fail.
But from the perspective of the test, I don't really care about those details. I just want to test that the method was called, and that a User with a given id was passed as the argument.

So is there some way to make call assertions where you only match against certain aspects of the arguments?
For instance:

test_with_mock "it works", %{conn: conn, user: user},
  get conn, "/"
  assert_called VisitorLog.track!(%User{id: user.id})
end

In Ruby rspec you could use something like the hash-including matcher:
https://rspec.info/documentation/3.9/rspec-mocks/RSpec/Mocks/ArgumentMatchers.html#hash_including-instance_method

Public function call is not mocked

If we have module Foo, wanna to test function bar/1

def bar(data) do
  buzz(data, @someparam)
end

Where buzz/2 is also public function. We try to mock buzz/2 function with :passthrough option, but it not get mocked - original function is called. If we rewrite bar/1 function like this

def bar(data) do
  Foo.buzz(data, @someparam)
end

then buzz/2 function can be mocked. I think this behaviour should be fixed of documented.

Mocking a whole module by providing alternative implementation

I need to mock huge parts of File, because I have to test a function which involves heavy creating and deleting of files and folders.

To make it a little bit easier to test, I don't wan't to check if File.rm("file") has been called but provide some own asserts like assertDeleted "file" which then is independent of the rm function used (rm, rm_rf and banged versions thereof).

To keep track of file operations and such it were much easier to just implement a module which does the mocking instead of providing the list of functions as done in the example.

meck version error (0.7.2 -> 0.8.1)

Hi, thank you for the nice library.
Recently, meck version seems to be updated from 0.7.2 to 0.8.1, and mock started showing the "the dependency does not match the requirement" error.

I tried to fix the version in the "mix.exs", but just updating it to the latest causes another errors at "mix test", which I couldn't fix. Do you have any idea?

$ git clone https://github.com/jjh42/mock.git
$ cd mock
$ mix deps.clean --all && mix deps.unlock --all && mix deps.get
...
* Compiling docs_ghpages
Unchecked dependencies for environment dev:
* meck [git: "git://github.com/eproxus/meck.git"]
  the dependency does not match the requirement 0.7.2, got 0.8.1
** (Mix) Can't continue due to errors on dependencies

Then, tried to update the mix.exs

diff --git a/mix.exs b/mix.exs
index 68efdf8..531361b 100644
--- a/mix.exs
+++ b/mix.exs
@@ -16,7 +16,7 @@ defmodule Mock.Mixfile do
   # Returns the list of dependencies in the format:
   # { :foobar, "0.1", git: "https://github.com/elixir-lang/foobar.git" }
   defp deps do
-    [ {:meck,"0.7.2", [github: "eproxus/meck"]}]
+    [ {:meck,"0.8.1", [github: "eproxus/meck"]}]
   end

Then retried "mix deps.get", but "mix test" showing the error. When I specified the "tag: 0.7.2" along with "github:" option, the "mix test" passes. So, it would be caused by the change in meck, but I couldn't identify how it's happening.

$ mix deps.clean --all && mix deps.unlock --all && mix deps.get
$ mix test
Compiled lib/mock.ex
Generated mock.app


  1) test called (MockTest)
     ** (EXIT) {:undefined_module, Dummy}
     stacktrace:
       src/meck_proc.erl:402: :meck_proc.resolve_can_expect/3
       src/meck_proc.erl:194: :meck_proc.init/1
       gen_server.erl:304: :gen_server.init_it/6
       proc_lib.erl:239: :proc_lib.init_p_do_apply/3

..

  2) test simple mock (MockTest)
     ** (EXIT) {:undefined_module, Dummy}
     stacktrace:
       src/meck_proc.erl:402: :meck_proc.resolve_can_expect/3
       src/meck_proc.erl:194: :meck_proc.init/1
       gen_server.erl:304: :gen_server.init_it/6
       proc_lib.erl:239: :proc_lib.init_p_do_apply/3

  3) test test_with_mock (MockTest)
     ** (EXIT) {:undefined_module, Dummy}
     stacktrace:
       src/meck_proc.erl:402: :meck_proc.resolve_can_expect/3
       src/meck_proc.erl:194: :meck_proc.init/1
       gen_server.erl:304: :gen_server.init_it/6
       proc_lib.erl:239: :proc_lib.init_p_do_apply/3



Finished in 0.4 seconds (0.1s on load, 0.3s on tests)
5 tests, 3 failures

Can not use mocked module in mock block

See this repository. The documentation of mock implies that a mocked module can be called in the mock block (e.g. to test a function of that module that calls the mocked function). However, doing so results in a module Foo is not available error.

with_mock and Behaviours

Hello folks,

I started to use Elixir and since I like to practice TDD/test-first-programming when I am at writing code I find useful to use this library to support me during the phase of roles discovery, which means I know I need to use a collaborator but I haven't enough information to implement it, I would like that the discovery of the role is guided by tests.

In Java, an example of an implementation of the concept of roles are interfaces. In Elixir we have Behaviours.

I tried to create a mock of a behaviour using this library but something is wrong, or I am missing something. Maybe it is not supported yet. Here is an example of what I am trying to do:

A role: collaborator.ex

defmodule Collaborator do
  @callback save(String.t) :: {:ok, String.t}
end

A test: a Client that need to use the Collaborator

  test "we call a collaborator" do
    with_mock Collaborator, [save: fn(_name) -> "this is a test" end] do
      response = Client.do_something()

      assert "this is a test" == response
    end
  end

A client

defmodule Client do
  @collaborator Collaborator

  def do_something() do
    @collaborator.save("something")
  end
end

At the moment if I try to run the test I get:

** (ErlangError) Erlang error: {:undefined_function, {Collaborator, :save, 1}}
     code: with_mock Collaborator, [save: fn(_name) -> "this is a test" end] do

Maybe I am missing something, or I get this because mock don't support Behaviours at time. Do you think that the support of Behaviours in mock can be a good feature to add?

Thanks,
joe.

Mocking functions with optional arguments

Variable amount of arguments is not allowed in lambda, how would one approach mocking a function with optional arguments then?

defmodule HigherMath do
  def times(x, m \\ 2), do: x * m # just some silly example
end
# .....
test "test_name" do
  with_mock HigherMath, [get: fn (x, m) -> :uh end] do
    times(3) # mock ignored, outputs 6
  end
end

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.