Testing error thrown by a macro

Is there a standard way to test that a macro throws an error for certain inputs? Consider the following example:

macro foo(x)
    x == :a || throw(ArgumentError("oops."))
    1
end

If I use @test_throws normally, it doesn’t catch the error:

julia> @test_throws ArgumentError @foo b
ERROR: LoadError: ArgumentError: oops.
Stacktrace:
 [1] var"@foo"(__source__::LineNumberNode, __module__::Module, x::Any)
   @ Main ./REPL[18]:2
in expression starting at REPL[19]:1

I can work around that by using @eval, but it seems clunky and I have to catch the LoadError instead of the ArgumentError:

julia> @test_throws LoadError @eval @foo b
Test Passed
      Thrown: LoadError

Is there a better way? I suppose I could split the expression handling into a separate function and test that function instead, but I’d rather there was a more general solution to this.

julia> @test_throws ArgumentError @macroexpand @foo b
Test Passed
      Thrown: ArgumentError
4 Likes

This is my go-to because I can interpolate stuff easier into an Expr instance.

julia> @test_throws ArgumentError macroexpand(Main, :(@foo b))
Test Passed
      Thrown: ArgumentError

Its @macroexpand expression does differ a bit in the macroexpand line from fredrikekre’s, so I’m not certain it behaves the same generally even though I expect it to.

2 Likes