Metaprogramming Elixir : How to deal with assertion in test macro of sample ExUnits one by one

September 2, 2017    Elixir

Environments

  • macOS sierra
  • Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
  • Elixir 1.5.1

What is this article about?

I’ve been learning macro with Metaprogramming Elixir written by Chris McCord and I’ve got troubled that test macro only returned last result.

If you’ve learned ExUnits sample code along that book, you’ve found that MathTest.run only showed one result for one test case.

I’ve decided to improve ExUnits sample to be able to render every assertion result

Today I write about how I solved this problem

If you aren’t familiar with Metaprogramming, probably you need to read Metaprogramming Elixir, because I won’t mention ExUnits sample code in detail

Before I introduce my ideas, Let’s check sample code quickly

math_test_final.exs && assertion.exs

If you want the perfect sample code, you can get not only this book but also sample source code on The Pragmatic Bookshelf web site1

To put it simply, when the MathTest module is compiled, test description is convertied to uniqe function and then that function and description are registered to @test to be invoked in Assertion.Test.run.

Also the uniqe function is definded in MathTest.

You can imagine MathTest became like following code. When the uniqe function is invoked, assert functions are invoked and that function return :ok or {:fail, reason} written in Assertion.Test.assert

Assertion.Test.run receive :ok or {:fail, reason} then render result.

But problem happens here. Function generally return value at last execution, so run shows following result.

MathTest has 4 assertions, but MathTest.run renders 2 result.

To solve this problem, I’ve came across the way of using process.

use process

I felt I wish I could render a result after every assertion finish.

So I tried to search how to handle function in function and then I felt process has a possibility to solve it.

I found I can use a function as a process within Elixir and I interpreted my concept to “I wish I could render a result after every process finish”.

I answered “Yes, I can control process”

At first I’ve checked my new concept works.

It works well. run send self PID and then assert send back message

But once process revceive a message, process in run stop receiving one. so I defined a recursive function in order to revceive a message repeatedly.

render_via_process function invoke itself at the end I got following result successfully. But as you can see above code, receive do clause is not DRY. I have to add it into new assert if I add new assert function.

And one more thing, Process in the run is still receiving.

To solve these happening, I generate receive clause with macro and use try catch to stop explicitly

This is the final code I got 5 result successfully :) That’s it!!

That is one of the good example of power of process, you know. This time I see run as a caller, and see test macro as a spawned process and then interact between these. I’m glad and exited to find that process is so interactive.

If you have any feedback, pleas leave a message.



comments powered by Disqus