Use macro to define function or test within same module

Hello,

I have here (a rather silly) example of defining and using a macro within the same module.

defmodule FooTest do
  use ExUnit.Case

  defmacro p(x) do
    quote do
      IO.puts(unquote(x))
    end
  end

  test "foo" do
    p(123)
  end
end

It works :smile:

18:53 $ mix test 
123
.

Finished in 0.01 seconds
1 test, 0 failures

Randomized with seed 450622

I do not really care if the macro is public or private.

Now, what Iā€™d really like to do is to use a ā€œwithin the sameā€ module macro to define tests.

This works if I create a separate Helper module.

defmodule Helper do
  defmacro testp(name, do: body) do
    quote do
      test(unquote(name)) do
        IO.puts "Running test #{unquote(name)}"
        unquote(body)
      end
    end
  end
end

defmodule FooTest do
  use ExUnit.Case
  import Helper

  testp "foo" do
    assert 1 == 1
  end
end
19:03 $ mix test 
Running test foo
.

Finished in 0.02 seconds
1 test, 0 failures

Randomized with seed 678670

But I do not get it to work within the same module.

defmodule FooTest do
  use ExUnit.Case

  defmacro testp(name, do: body) do
    quote do
      test(unquote(name)) do
        IO.puts "Running test #{unquote(name)}"
        unquote(body)
      end
    end
  end

  testp "foo" do
    assert 1 == 1
  end
end

This gives

19:03 $ mix test 
** (CompileError) test/foo_test.exs:13: undefined function testp/2
    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
    (elixir) lib/code.ex:370: Code.require_file/2
    (elixir) lib/kernel/parallel_require.ex:57: anonymous fn/2 in Kernel.ParallelRequire.spawn_requires/5

Am I missing something? Or is this technically not possible?

I am using

19:04 $ elixir -v
Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Elixir 1.4.2

Cheers
Kilian

2 Likes

Macroā€™s should really always be defined in a module where they are not used in my experience. :slight_smile:

3 Likes