Repository: edgurgel/mimic
Pull request #82 (2024-10-02 03:07:30)

enable calling Mimic.allow when not using expectations
We have a test case for GRPC that calls Mimic.allow across many modules, some of which don't also call Mimic.expect in every  test case causing the function to raise. Is there a reason to not allow this, or potentially allow this behavior behind an opt?


```
     20:57:32.287 [error] GenServer Mimic.Server terminating
     ** (CaseClauseError) no case clause matching: []
         (mimic 1.10.1) lib/mimic/server.ex:402: Mimic.Server.handle_call/3
         (stdlib 5.2.3) gen_server.erl:1131: :gen_server.try_handle_call/4
         (stdlib 5.2.3) gen_server.erl:1160: :gen_server.handle_msg/6
         (stdlib 5.2.3) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
     Last message (from #PID<0.619.0>): {:allow, Auth.ApiKeys, #PID<0.604.0>, #PID<0.619.0>}
     20:57:32.294 [error] ** (exit) exited in: GenServer.call(Mimic.Server, {:allow, Auth.ApiKeys, #PID<0.604.0>, #PID<0.619.0>}, 5000)
         ** (EXIT) an exception was raised:
             ** (CaseClauseError) no case clause matching: []
                 (mimic 1.10.1) lib/mimic/server.ex:402: Mimic.Server.handle_call/3
                 (stdlib 5.2.3) gen_server.erl:1131: :gen_server.try_handle_call/4
                 (stdlib 5.2.3) gen_server.erl:1160: :gen_server.handle_msg/6
                 (stdlib 5.2.3) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
         (elixir 1.16.2) lib/gen_server.ex:1114: GenServer.call/3
         (mimic 1.10.1) lib/mimic.ex:342: Mimic.allow/3
         (auth 0.1.0) test/support/grpc_test_allowance.ex:23: AuthGRPC.Interceptors.TestAllowance.call/4
         (grpc 0.9.0) lib/grpc/telemetry.ex:96: anonymous fn/2 in GRPC.Telemetry.server_span/5
         (telemetry 1.2.1) /Users/michael/code/auth/deps/telemetry/src/telemetry.erl:321: :telemetry.span/3
         (grpc 0.9.0) lib/grpc/telemetry.ex:95: GRPC.Telemetry.server_span/5
         (grpc 0.9.0) lib/grpc/server/adapters/cowboy/handler.ex:532: GRPC.Server.Adapters.Cowboy.Handler.do_call_rpc/3
         (grpc 0.9.0) lib/grpc/server/adapters/cowboy/handler.ex:505: GRPC.Server.Adapters.Cowboy.Handler.call_rpc/3
         (stdlib 5.2.3) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
```

michaelst: I just found another case that I'm not sure how to handle, unlike Hammox we can't call allow before expect. For example when starting up a GenServer I pass an allow_fun opt that is run at startup, and then later throughout the test call different expects.
edgurgel: Thanks for starting this @michaelst ! I think I understand the issue you are reporting but would you be able to give an example on how this is a problem? Or a test case that shows this problem?
michaelst: I opened two issues with more details
edgurgel: Thanks! Will check it out this week!

Patch:
diff --git a/lib/mimic/server.ex b/lib/mimic/server.ex
index 12865a1..abf61d5 100644
--- a/lib/mimic/server.ex
+++ b/lib/mimic/server.ex
@@ -402,6 +402,9 @@ defmodule Mimic.Server do
     case :ets.lookup(__MODULE__, {owner_pid, module}) do
       [{{^owner_pid, ^module}, actual_owner_pid}] ->
         :ets.insert(__MODULE__, {{allowed_pid, module}, actual_owner_pid})
+
+      [] ->
+        :ets.insert(__MODULE__, {{allowed_pid, module}, owner_pid})
     end
 
     {:reply, {:ok, module}, state}


Test patch:
diff --git a/test/mimic_test.exs b/test/mimic_test.exs
index 769dffc..4b0d355 100644
--- a/test/mimic_test.exs
+++ b/test/mimic_test.exs
@@ -796,6 +796,38 @@ defmodule Mimic.Test do
       assert mult_result == :stubbed
     end
 
+    test "allows different processes to share mocks from parent process when allow is defined first" do
+      parent_pid = self()
+
+      child_pid =
+        spawn_link(fn ->
+          receive do
+            :call_mock ->
+              add_result = Calculator.add(1, 1)
+              mult_result = Calculator.mult(1, 1)
+              send(parent_pid, {:verify, add_result, mult_result})
+          end
+        end)
+
+      Calculator
+      |> allow(self(), child_pid)
+      |> expect(:add, fn _, _ -> :expected end)
+      |> stub(:mult, fn _, _ -> :stubbed end)
+
+      send(child_pid, :call_mock)
+
+      assert_receive {:verify, add_result, mult_result}
+      assert add_result == :expected
+      assert mult_result == :stubbed
+    end
+
+    test "doesn't raise if no expectation defined" do
+      child_pid = spawn_link(fn -> :ok end)
+
+      Calculator
+      |> allow(self(), child_pid)
+    end
+
     test "allows different processes to share mocks from child process" do
       parent_pid = self()