Giter VIP home page Giter VIP logo

mimic's People

Contributors

alimovsv avatar bbalser avatar calvin-kargo avatar cassiomarques avatar dependabot-preview[bot] avatar dylan-chong avatar edgurgel avatar fartek avatar jared-mackey avatar jeffgrunewald avatar jmnsf avatar joshprice avatar kianmeng avatar moskyb avatar pmargreff avatar sevenseacat avatar tony612 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

mimic's Issues

Issue with dialyzer attributes in copied modules.

Hey my dude.

When attempting to copy a module which contains @dialyzer {:nowarn_function, [&Cachex.get/2, &Cachex.put/4]} I get the following compilation error:

** (CaseClauseError) no case clause matching: {:error, [{'lib/core_gateway/session/cache.ex', [{1, :erl_lint, {:bad_dialyzer_attribute, {:nowarn_function, &Cachex.get/2}}}, {1, :erl_lint, {:bad_dialyzer_attribute, {:nowarn_function, &Cachex.put/4}}}]}], []}
    (mimic 1.5.0) lib/mimic/module.ex:43: Mimic.Module.rename_module/2
    (mimic 1.5.0) lib/mimic/module.ex:27: Mimic.Module.replace!/1
    (mimic 1.5.0) lib/mimic.ex:344: Mimic.copy/1
    test/test_helper.exs:4: (file)

Which is super weird because the attribute is valid and works when compiling normally. Whether or not I should be opening a PR to fix Cachex's typespecs is beyond the scope of this issue ๐Ÿ˜

Hope you're well.
James.

Not properly resetting cover modules between app test runs in umbrella apps

Hi, I'm having an issue running my test suite from the root of my umbrella app. Here's the error:

11:25:03.762 [error] Task #PID<0.1852.0> started from Mimic.Server terminating
** (MatchError) no match of right hand side value: {:error, {:cant_open_file, '/apps/app_1/Elixir.App1-566835.coverdata', :enoent}}
    (mimic 1.7.1) lib/mimic/cover.ex:32: Mimic.Cover.replace_coverdata!/3
    (elixir 1.13.3) lib/task/supervised.ex:89: Task.Supervised.invoke_mfa/2
    (elixir 1.13.3) lib/task/supervised.ex:34: Task.Supervised.reply/4
    (stdlib 3.17.1) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
Function: #Function<9.80215867/0 in Mimic.Server.handle_call/3>
    Args: []
11:25:03.769 [error] Task #PID<0.1667.0> started from #PID<0.94.0> terminating
** (stop) exited in: GenServer.call(Mimic.Server, {:reset, App1}, 60000)
    ** (EXIT) an exception was raised:
        ** (MatchError) no match of right hand side value: {:error, {:cant_open_file, '/apps/app_1/Elixir.App1-566835.coverdata', :enoent}}
            (mimic 1.7.1) lib/mimic/cover.ex:32: Mimic.Cover.replace_coverdata!/3
            (elixir 1.13.3) lib/task/supervised.ex:89: Task.Supervised.invoke_mfa/2
            (elixir 1.13.3) lib/task/supervised.ex:34: Task.Supervised.reply/4
            (stdlib 3.17.1) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
    (elixir 1.13.3) lib/gen_server.ex:1030: GenServer.call/3
    (elixir 1.13.3) lib/enum.ex:937: Enum."-each/2-lists^foreach/1-0-"/2
    (ex_unit 1.13.3) lib/ex_unit/runner.ex:64: ExUnit.Runner.run_with_trap/2
    (ex_unit 1.13.3) lib/ex_unit/runner.ex:31: ExUnit.Runner.run/2
    (elixir 1.13.3) lib/task/supervised.ex:89: Task.Supervised.invoke_mfa/2
    (elixir 1.13.3) lib/task/supervised.ex:34: Task.Supervised.reply/4
    (stdlib 3.17.1) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
Function: #Function<0.129552615/0 in ExUnit.async_run/0>
    Args: []
** (EXIT from #PID<0.94.0>) exited in: GenServer.call(Mimic.Server, {:reset, App1}, 60000)
    ** (EXIT) an exception was raised:
        ** (MatchError) no match of right hand side value: {:error, {:cant_open_file, '/apps/app_1/Elixir.App1-566835.coverdata', :enoent}}
            (mimic 1.7.1) lib/mimic/cover.ex:32: Mimic.Cover.replace_coverdata!/3
            (elixir 1.13.3) lib/task/supervised.ex:89: Task.Supervised.invoke_mfa/2
            (elixir 1.13.3) lib/task/supervised.ex:34: Task.Supervised.reply/4
            (stdlib 3.17.1) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

I went and added some IO.puts into the mimic lib to see if I can hunt it down a bit.

  1. Added the COPIED: text to ensure_module_copied where it gets the coverdata_path from Module.replace!
  2. Added Replacing cover data for in replace_coverdata! and am printing out the original_coverdata_path and path
  3. Added Deleting in replace_coverdata! and am printing out both paths right before it delets.

Here's the results (trimmed up a bit wit some renaming)

==> app_1
Cover compiling modules ...
COPIED: /apps/app_1/Elixir.App1-566835.coverdata
COPIED: /apps/app_1/Elixir.App1.Thing-566835.coverdata

...mix test output...

Randomized with seed 421312
Replacing cover data for original: /apps/app_1/Elixir.App1.Thing-566835.coverdata
Replacing cover data for original: /apps/app_1/Elixir.App1-566835.coverdata
Replacing cover data for path: /apps/app_1/Elixir.App1.Thing.Mimic.Original.Module-566835.coverdata
Replacing cover data for path: /apps/app_1/Elixir.App1.Mimic.Original.Module-566835.coverdata
Deleting /apps/app_1/Elixir.App1.Thing.Mimic.Original.Module-566835.coverdata
Deleting /apps/app_1/Elixir.App1.Thing-566835.coverdata
Deleting /apps/app_1/Elixir.App1.Mimic.Original.Module-566835.coverdata
Deleting /apps/app_1/Elixir.App1-566835.coverdata

Generating cover results ...

...coverage results output...

Analysis includes data from imported files
["/apps/app_1/Elixir.App1.Thing-566835.coverdata",
 "/apps/app_1/Elixir.App1.Thing.Mimic.Original.Module-566835.coverdata"]
Analysis includes data from imported files
["/apps/app_1/Elixir.App1-566835.coverdata",
 "/apps/app_1/Elixir.App1.Mimic.Original.Module-566835.coverdata"]
Generated HTML coverage results in "cover" directory

==> app_2
Cover compiling modules ...

...mix test output...

Randomized with seed 421312
Replacing cover data for original: /apps/app_1/Elixir.App1-566835.coverdata
Replacing cover data for path: /apps/app_2/Elixir.App1.Mimic.Original.Module-566835.coverdata

11:25:03.762 [error] Task #PID<0.1852.0> started from Mimic.Server terminating
...rest of the same error...

The problem seems to exist in the second app. It appears to be using the wrong path for the original coverdata path. The file is actually located at /apps/app_2/Elixir.App1-566835.coverdata. It stills has the old path from when app_1 test had ran and did not properly clear out between apps test runs. It also doesn't seem to copy the module (COPIED) and so it's path stored in the state is still the same from the original copy from it running in the first app.

Mimic failed copy module on version 1.2.0

Hi, when i was using Mimic (version 1.2.0) as mock library, i was stumped by this error

** (UndefinedFunctionError) function :cover.compile_beams/1 is undefined or private. Did you mean one of:

      * compile/1
      * compile/2
      * compile_beam/1
      * compile_beam_directory/0
      * compile_beam_directory/1

    :cover.compile_beams([{CoronaNews.Gateway.Mimic.Original.Module, <<70, 79, 82, 49, 0, 0, 16, 208, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 2, 31, 0, 0, 0, 43, 47, 69, 108, 105, 120, 105, 114, 46, 67, 111, 114, 111, 110, 97, 78, 101, 119, 115, 46, 71, 97, 116, 101, ...>>}])
    lib/mimic/module.ex:45: Mimic.Module.rename_module/2
    lib/mimic/module.ex:27: Mimic.Module.replace!/1
    lib/mimic.ex:338: Mimic.copy/1
    test/test_helper.exs:1: (file)

only when i revert mimic to version 1.0.0 that this error fixed
Here is code on test_helper.exs

# at test_helper.ex
Mimic.copy(CoronaNews.Gateway)
Application.ensure_started(:mimic)
ExUnit.start()

here is my iex -v

Erlang/OTP 22 [erts-10.6.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

IEx 1.9.4 (compiled with Erlang/OTP 21)

Mimic is not using a stub function if that function is called by a function in the same module

Hello,
So recently i realize if you call a function that have a stub with a function in the same module that does not have stub.
The second function is actually calling the original function of the stubbed function.
Is there a way to overcome this limitation and make the second function to call the stub instead of the original function.

Thank you.
PS: If it's not possible it's alright, I'll create a separate module that will be the one that i make as a module for stubbed function.

Mimic takes half a second to start up

this isn't a terribly big deal but at my company we've got a really slow start up time for tests. Currently it takes 13 seconds just to start up the test (not even run the test, not even compile either).

The biggest culprit is Prometheus, which is taking up 8-9 seconds of application start up time. Removing that library and stubbing out all it's modules, mimic comes in 2nd place at 481ms.

Wow 481ms? That's not a lot! :D but when it comes to having a fast TDD cycle, it's annoying to have to wait 4 seconds (even 13!) for unit test to start running. (Mind you, I'm also trying to figure out why the other libraries are also taking their time)

A way to show mimic takes half a second is by running

> time MIX_ENV=prod mix run
warning: use Mix.Config is deprecated. Use the Config module instead
  config/config.exs:1


real    0m0.937s
user    0m0.744s
sys     0m0.314s

Dylan@DylansWorkMBPM1:(main)~/Development/solve/mimic
> time MIX_ENV=prod mix run --no-start
warning: use Mix.Config is deprecated. Use the Config module instead
  config/config.exs:1


real    0m0.526s
user    0m0.330s
sys     0m0.319s

Both commands have compilation/IO overheads, the latter doesn't start up any applications. (prod env to avoid compiling dialyzer and such). The difference is about 400ms.

I'm curious really, why does it take almost half a second to start up Mimic? I understand that they're likely won't be any changes, but I'm curious than anything right now.

Question: arguments pattern matching.

Is the Mimic expected behavior to fail an expectation if the original implementation gets called with different arguments than the one defined in the expect function?

defmodule Test do
 def call(arg), do: true
end


Test
|> expect(:call, 1, fn 2 -> true end)

Test.call(3)

Is the above supposed to fail verification?

Unable to stub `Integer.to_string/2`

Issue

Trying to stub the Integer.to_string/2 method does not work.

Environment

  • Elixir 1.11.4 (compiled with Erlang/OTP 23)
  • Erlang/OTP 24 [erts-12.0.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]

Steps to reproduce

# test_helper.exs
Mimic.copy(Integer)

ExUnit.start()
# test/sometest.exs
defmodule SomeTest do
  use ExUnit.Case
  use Mimic

  test "greets the world" do
    stub(Integer, :to_string, fn _ -> "STUB" end)
    stub(Integer, :to_string, fn _, _ -> "STUB" end)

    random = Integer.to_string(5)

    assert random == "STUB"
  end
end

It seems the above test will fail, but if it's for some other method, like Integer.digits/1 it passes.

Not really sure what could be the issue without further investigation. ๐Ÿค”

EDIT: I'll be more than happy to open an example repo reproducing the issue ๐Ÿ˜„

Faster/Optional Mimic.copy for TDD

Hi @edgurgel ,

Is it good to make Mimic.copy optional/faster so that starting mix test would be faster?

recently i was practicing TDD at a project at work which use Mimic a lot, during that time, i realize that Mimic.copy is taking a long time, making the starting of test (mix app.start test/some_test.exs) become really slow. that project was having Mimic.copy ~100 module. and the reason Mimic.copy is slow is because one of the operation, rename_module is slow, it was decompiling & recompiling module to rename it. if the module size is big, then rename_module would take long time to finish.

currently i was hacking it to make sure when i was in TDD_MODE, test_helper.exs would disable most of Mimic.copy, with this i reduce test startup time from ~45 second to ~5 second.

I was considering on how to solve this problem in general, some ideas:

  • make rename module faster, currently it's decompiling & recompiling, which is slow. quick google search find https://github.com/trapexit/erlang-hacks/blob/master/beam/src/beam_renamer.erl which able to rename it on OTP BEAM file byte level, but i am not sure if this work / how to proceed with it.
  • enable on demand Mimic.copy, the idea is at test file, there's a function which would run Mimic.copy if a particular module isn't copied before (with unique single worker to make sure async test doesn't have problem), it looks somewhat like this
#test_helper.exs
if not tdd_mode? do
  Mimic.copy(MockedModule)
  ...
end

#test/some_test.exs
defmodule SomeTest do
  setup do
    Mimic.ensure_copied!(MockedModule)
  end

  test "some test" do
    Mimic.stub(MockedModule ....)
    ...
  end
end

Elixir version: 1.10.3, OTP 22, Mimic version 1.5.0

simple api for verifying reject before another expect/stub

Lets say, we've following module & test:

defmodule Calculator do
  def calculate(:div, num1, num2) do
    divide(num1, num2)
  end

  def calculate(_, num1, num2) do
    // do other things
  end

  def divide(num1, 0), do: :error
  def divide(num1, num2), do: num1 / num2
end
reject(&Calculator.divide/2)
Calculator.calculate(:add, 3, 4)

expect(Calculator, :divide, fn _, _ -> 42 end)
Calculator.calculate(:div, 100, 5)

Right now, it gives Mimic.UnexpectedCallError which makes sense. I wanted to assert no call was made on the first round but on second round, it does make call and the expect should work but reject needs to be invalidated at this point via some construct.

I tried doing:

reject(&Calculator.divide/2)
Calculator.calculate(:add, 3, 4)

verify!()

expect(Calculator, :divide, fn _, _ -> 42 end)
Calculator.calculate(:div, 100, 5)

But it does not seem to work. Can we have a simple API to achieve this? Of course, in most cases, we can probably split the test into two cases but this is just a contrived example and sometimes for real world stuff, need that within a single test case.

Being able to call the original mocked function would be amazing.

There are many cases where I want to call the original function some number of times before then wanting to do something different, or perhaps wanting to call the original function conditionally dependent on the arguments.

I'm not aware of a current way of grabbing the original function. When I attempt this:

original = &MyModule.function/3

even if done before calling expect, I get the mocked function.

Not properly copying modules with NIFs

Hey!

I have a project where we use Mimic to copy a few modules, worked perfectly until we tried to copy a module that was basically a NIF module:

# nif module
defmodule Core.Crypto.Ethereum do
  use Rustler, otp_app: :core, crate: :core_crypto_ethereum

  def ens_lookup_address(_), do: :erlang.nif_error(:nif_not_loaded)
end

# test_helpers.exs
# ...
# a bunch of Mimic.copy/1 calls here
Mimic.copy(Core.Crypto.Ethereum)

ExUnit.start()

# test:

defmodule MyTest do
  use Core.DataCase, async: true

  import Mimic

  describe "some function" do
    test "example" do
      expect(Core.Crypto.Ethereum, :ens_lookup_address, fn _ -> {:ok, "example.eth"} end)

      assert true
    end
  end
end

Output:

** (ArgumentError) Module Core.Crypto.Ethereum has not been copied.  See docs for Mimic.copy/1
     code: expect(Ethereum, :ens_lookup_address, fn _addr -> {:ok, "example.eth"} end)
     stacktrace:
       (mimic 1.7.4) lib/mimic.ex:498: Mimic.validate_server_response/2
       test/core/services/my_test.exs:49: (test)

# ...

18:43:45.063 [error] GenServer Mimic.Server terminating
** (stop) {:error_loading_module, Core.Crypto.Ethereum.Mimic.Original.Module, :on_load_failure}
    (mimic 1.7.4) lib/mimic/module.ex:87: Mimic.Module.load_binary/3
    (mimic 1.7.4) lib/mimic/module.ex:57: Mimic.Module.rename_module/2
    (mimic 1.7.4) lib/mimic/module.ex:34: Mimic.Module.replace!/1
    (mimic 1.7.4) lib/mimic/server.ex:504: Mimic.Server.ensure_module_copied/2
    (mimic 1.7.4) lib/mimic/server.ex:360: Mimic.Server.handle_call/3
    (stdlib 4.0.1) gen_server.erl:1146::gen_server.try_handle_call/4
    (stdlib 4.0.1) gen_server.erl:1175::gen_server.handle_msg/6
    (stdlib 4.0.1) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Last message (from #PID<0.2767.0>): {:expect, {Core.Crypto.Ethereum, :ens_lookup_address, #Function<4.71929683/1 in Core.MyTest."test example"/1>, 1}, 1, #PID<0.2767.0>}

`Protocol.UndefinedError` when printing out a struct that is from a stubbed module

This was a super-bizarro one that I just came across! I've tried to minimize the example as much as I can.

Given a module that defines a struct, eg.

defmodule Log do
  defstruct [:name]
  def hello, do: nil
end

I can use that struct quite happily in tests -

# in test
dbg(%Log{})

# prints out
%Log{} #=> %Log{name: nil}

but if I want to stub out a function on that module, it throws up a really confusing error

# in test 
stub(Log, :hello, fn -> "hello" end)
dbg(%Log{})

# prints out 
%Log{} #=> #Inspect.Error<
  got Protocol.UndefinedError with message:

      """
      protocol Enumerable not implemented for nil of type Atom. This protocol is implemented for
      the following type(s): DBConnection.PrepareStream, DBConnection.Stream, Date.Range, 
      Ecto.Adapters.SQL.Stream, File.Stream, Floki.HTMLTree, Function, GenEvent.Stream, HashDict, 
      HashSet, IO.Stream, IP.Prefix, Jason.OrderedObject, List, Map, MapSet, MerkleMap, 
      Phoenix.LiveView.LiveStream, Postgrex.Stream, Range, Stream, StreamData
      """

  while inspecting:

      %{__struct__: Log, name: nil}

  Stacktrace:

    (elixir 1.14.3) lib/enum.ex:1: Enumerable.impl_for!/1
    (elixir 1.14.3) lib/enum.ex:166: Enumerable.reduce/3
    (elixir 1.14.3) lib/enum.ex:4307: Enum.reduce/3
    (elixir 1.14.3) lib/inspect.ex:595: Inspect.Any.inspect/2
    (elixir 1.14.3) lib/inspect/algebra.ex:341: Inspect.Algebra.to_doc/2
    (elixir 1.14.3) lib/kernel.ex:2254: Kernel.inspect/2
    (elixir 1.14.3) lib/macro.ex:2576: Macro.dbg_format_ast_to_debug/2
    (elixir 1.14.3) lib/macro.ex:2546: Macro.__dbg__/3
    test/my_app/my_test.exs:44: MyApp.MyTest."test stuff"/1
    (ex_unit 1.14.3) lib/ex_unit/runner.ex:512: ExUnit.Runner.exec_test/1
    (stdlib 4.3.1) timer.erl:235: :timer.tc/1
    (ex_unit 1.14.3) lib/ex_unit/runner.ex:463: anonymous fn/4 in ExUnit.Runner.spawn_test_monitor/4
>

I can't work out if this is an error in displaying the struct, or actually using the struct for things. As I want to use the struct in assertions for tests, trying to print out test failures is itself raising protocol errors like this, and it's rather confusing!

Error copying `Code` module

I mimicked Code module so I could test with and without optional dependencies

Code
|> expect(:ensure_loaded?, fn Mojito -> true end)
|> expect(:ensure_loaded?, fn Jason -> false end)

Everything looked like it was working well, and tests passing, but then I get this ** (EXIT from #PID<0.93.0>) killed error right after the test run and gives me an exit code of 1, which will mess up CI. :(

Finished in 0.07 seconds (0.07s async, 0.00s sync)
6 tests, 0 failures

Randomized with seed 27160
** (EXIT from #PID<0.93.0>) killed

If I comment out the tests that use Code mocks, I still get the error until I comment out Mimic.copy(Code) from test helpers.

Is there any hope of this working, or anything I can do?

Inspecting existing stubs/expectations

I'm writing some generic code for my tests that calls Mimic's allow/3 on Task.async when in the test environment, so that I can write async code without worrying whether it's going to be testable or not.

The problem is if no stubs or expectations were created for the Mock, the allow/3 call raises since there's no case for an empty list. I'm working around this in my own code by calling :ets directly as if I were Mimic, but would prefer a less hacky solution.

I could do a PR for this, but there are multiple possible solutions, wondering what you think would be preferrable:

  1. Adding an empty list case as a no-op in allow.
  2. Adding an empty list case but returning an error/warning instead.
  3. Exposing a function in the Mimic module for inspecting the existing stubs/expectations on a Mock.
  4. something else?

Thanks!

Edit: it'd also be useful to access the currently active mode, since we can't allow when :global. I'm using :sys.get_state(Mimic.Server) atm as a workaround.

Mimic can't see a function when it's called internally without the module name.

Hey Folks. I noticed Mimic is not able to see a function when this function is called without its module name.
For example:

defmodule Transactions do 
  def delete(guids) do 
    delete_something(guids)
  end 

  def delete_something(guids) do 
    {:ok, guids}
  end
end

Mimic.copy(Transactions)

Transactions
|> expect(:delete_something, fn transaction_guids ->
  IO.inspect("Hey")
  {:ok, transaction_guids}
end)

Transactions.delete([])

** (Mimic.VerificationError) error while verifying mocks for #PID<0.2950.0>:
     
* expected Transactions.delete_something/1 to be invoked 1 time(s) but it has been called 0 time(s)

To fix this we need to call this way:

__MODULE__.delete_something(guids)

Can I open a PR to fix this? What do you folks think?

Adding support for `stubs_with`

Hi @edgurgel , would adding support for stubs_with functionality, a function to completely mock a module with another module, is something that align with Mimic?

stubs_with would have similar behaviour to Mox, which is:

  1. it would completely redirect all of call to original module in test to stubbed module
  2. It would take lowest precedence, so if there's a new stub / expect at test code, that will take higher precedence

short example that i can think of is like

defmodule RedisCache do
  def fetch_cache(key), do: ...
  def put_cache(key, value), do: ...
end
....
defmodule TestRedisCache do
  # SImple cache by process dictionary
  def get_cache(key), do: Process.get(key, :none_value)
  end
  def put_cache(key, value), do: Process.put(key, value)
end
....
#test_helper.ex
Mimic.stubs_with(RedisCache, TestRedisCache)

error copying erlang modules

when trying to copy erlang modules there is an error printed out and copying doesn't succeed, is this intended? how can i copy/mock these kind of modules? thanks

15:32:21.833 [error] Can't load module ':rpc' that resides in sticky dir

  1) test success registering with manager (Worker.RegisterTest)
     apps/worker/test/regiser_test.exs:7
     ** (ArgumentError) Module :rpc has not been copied.  See docs for Mimic.copy/1
     code: expect(:rpc, :call, fn args ->
     stacktrace:
       (mimic) lib/mimic.ex:423: Mimic.raise_if_not_copied!/1
       (mimic) lib/mimic.ex:200: Mimic.expect/4
       test/regiser_test.exs:9: (test)

Mimic.expect/4 behaviour on number of calls being exceeded surprised me

Version 1.7.4

Hi, I've noticed that if the number of calls exceeds the expected count (default 1) on a mocked function, it reverts to calling the original function. I would have expected it to raise an error.

Is the behavior deliberate? I wanted to check before raising a documentation PR.

(I've a workaround involving initially stubbing with a stub-which-flunks (tm)).

Retrieving call count to easily make assertions on call count

currently, the expect/3 api only performs call assertion at the end of the test case. However, there are situations where it is ideal to make call assertions throughout the test case (e.g. that api calls are made correctly, args are correct, etc), much like how :meck exposes a few functions to make call assertions easier.

Are there plans for such an API? I can dig into this and raise a PR if so.

at least/at most number of calls with mimic expect/4

Currently, expect/4 takes in an optional parameter for the amount of calls expected for the function.

If this value is not provided, it seems to default to 1.

We have a test scenario that a function might be called once or twice, and either case we just want the function to return the same mock we are setting up. (ie expect(Module, :method_name, fn _ -> :ok end))

Is there a way to accomplish this with mimic? If not, would it be possible to add this as a functionality? Thank you!

Latest version of mimic breaks test coverage generation

When I update mimic to the latest version in a project I work on it breaks test coverage.

12:43:43.169 [error] Task #PID<0.13868.0> started from Mimic.Server terminating
** (MatchError) no match of right hand side value: {:error, {:not_cover_compiled, Synapse.AppRequest.Application.Mimic.Original.Module}}
    (mimic 1.7.4) lib/mimic/cover.ex:52: Mimic.Cover.export_coverdata!/1
    (mimic 1.7.4) lib/mimic/cover.ex:32: Mimic.Cover.clear_module_and_import_coverdata!/3
    (elixir 1.14.2) lib/task/supervised.ex:89: Task.Supervised.invoke_mfa/2
    (elixir 1.14.2) lib/task/supervised.ex:34: Task.Supervised.reply/4
    (stdlib 4.2) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Function: #Function<9.132716098/0 in Mimic.Server.handle_call/3>

Mimic.copy could be idempotent

When running mix test--stale from the top level of an umbrella app, and you have Mimic.copy(M) in two different app test_helper files, you'll get this error upon rerunning the tests with --stale (dunno why it doesnt happen without stale).

** (ArgumentError) Module M has already been copied.  See docs for Mimic.copy/1
    (mimic 1.7.2) lib/mimic.ex:506: Mimic.validate_server_response/2
    test/test_helper.exs:1: (file)
    (elixir 1.13.1) lib/code.ex:1183: Code.require_file/2

Unsure what the underlying issue is, but if Mimic ignored double copys then we'd maybe be ok.

UndefinedFunctionError while compiling on Elixir 1.9 with Erlang 22.

Hey Jerk.

I tried to add Mimic to a project which hasn't been reupped to the latest and greatest and got the following error at compile time:

** (UndefinedFunctionError) function :cover.compile_beams/1 is undefined or private. Did you mean one of:

      * compile/1
      * compile/2
      * compile_beam/1
      * compile_beam_directory/0
      * compile_beam_directory/1

    :cover.compile_beams([{Backbone.Charge.HookHandler.Mimic.Original.Module, <<70, 79, 82, 49, 0, 0, 36, 32, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 4, 48, 0, 0, 0, 65, 56, 69, 108, 105, 120, 105, 114, 46, 66, 97, 99, 107, 98, 111, 110, 101, 46, 67, 104, 97, 114, 103, 101, ...>>}])
    lib/mimic/module.ex:45: Mimic.Module.rename_module/2
    lib/mimic/module.ex:27: Mimic.Module.replace!/1
    lib/mimic.ex:343: Mimic.copy/1
    test/test_helper.exs:3: (file)

The output of elixir --version:

Erlang/OTP 22 [erts-10.6.4] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [hipe]

Elixir 1.9.4 (compiled with Erlang/OTP 22)

I had to go all the way back to Mimic 1.0.0 to get it to work.

Thanks!

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.