From c5b170cf1aedffc2ab90864ebb3e2369810925e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jhan=20S=2E=20=C3=81lvarez?= Date: Sat, 23 Aug 2025 21:12:17 -0500 Subject: [PATCH 1/2] fix: separate functions and macros for elixir modules --- lib/mimic/module.ex | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/mimic/module.ex b/lib/mimic/module.ex index e9c221c..723d669 100644 --- a/lib/mimic/module.ex +++ b/lib/mimic/module.ex @@ -117,8 +117,9 @@ defmodule Mimic.Module do mimic_info = module_mimic_info(opts) mimic_behaviours = generate_mimic_behaviours(module) mimic_functions = generate_mimic_functions(module) + mimic_macros = generate_mimic_macros(module) mimic_struct = generate_mimic_struct(module) - quoted = [mimic_info, mimic_struct | mimic_behaviours ++ mimic_functions] + quoted = [mimic_info, mimic_struct | mimic_behaviours ++ mimic_functions ++ mimic_macros] Module.create(module, quoted, Macro.Env.location(__ENV__)) module end @@ -175,7 +176,14 @@ defmodule Mimic.Module do defp generate_mimic_functions(module) do internal_functions = [__info__: 1, module_info: 0, module_info: 1] - for {fn_name, arity} <- module.module_info(:exports), + functions = + if function_exported?(module, :__info__, 1) do + module.__info__(:functions) + else + module.module_info(:exports) + end + + for {fn_name, arity} <- functions, {fn_name, arity} not in internal_functions do args = Macro.generate_arguments(arity, module) @@ -187,6 +195,26 @@ defmodule Mimic.Module do end end + defp generate_mimic_macros(module) do + macros = + if function_exported?(module, :__info__, 1) do + module.__info__(:macros) + else + [] + end + + for {macro_name, arity} <- macros do + args = Macro.generate_arguments(arity, module) + macro_fun = String.to_existing_atom("MACRO-#{macro_name}") + + quote do + defmacro unquote(macro_name)(unquote_splicing(args)) do + Server.apply(__MODULE__, unquote(macro_fun), [__CALLER__ | unquote(args)]) + end + end + end + end + defp generate_mimic_behaviours(module) do module.module_info(:attributes) |> Keyword.get_values(:behaviour) From b21c6d9f0e56d3520763fb64465810bf01388d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jhan=20S=2E=20=C3=81lvarez?= Date: Sat, 23 Aug 2025 21:13:23 -0500 Subject: [PATCH 2/2] fix: test mocking a module with macro --- test/mimic_test.exs | 11 +++++++++++ test/support/test_modules.ex | 3 +++ 2 files changed, 14 insertions(+) diff --git a/test/mimic_test.exs b/test/mimic_test.exs index a225ec6..7fdc37d 100644 --- a/test/mimic_test.exs +++ b/test/mimic_test.exs @@ -489,6 +489,17 @@ defmodule Mimic.Test do expect(Calculator, :add, 0, fn x, y -> x + y end) end end + + test "macros" do + expect(Calculator, :add, fn x, _y -> x + 2 end) + + quote do + require Calculator + + assert Calculator.add(4, 2) == Calculator.add_macro(4, 2) + end + |> Code.eval_quoted() + end end describe "expect/4 global mode" do diff --git a/test/support/test_modules.ex b/test/support/test_modules.ex index ab167ef..b1c40d5 100644 --- a/test/support/test_modules.ex +++ b/test/support/test_modules.ex @@ -1,6 +1,8 @@ defmodule AddAdapter do @moduledoc false @callback add(number(), number()) :: number() + @macrocallback add_macro(Macro.t(), Macro.t()) :: Macro.t() + @optional_callbacks add_macro: 2 end defmodule MultAdapter do @@ -13,6 +15,7 @@ defmodule Calculator do @behaviour AddAdapter @behaviour MultAdapter def add(x, y), do: x + y + defmacro add_macro(x, y), do: {:+, [], [x, y]} def mult(x, y), do: x * y end