Yevhenii Kurtov (3) [Avatar] Offline
#1
Hello, Saša!

First off let me express my gratitude for such an awesome book - it's very practical yet impressively transparent. Hard topics are explained in a very simple manner, which is quite amazing.

Right now I'm about to start an exercise 7.3.5: adding pooling and synchronizing DatabaseWorkers and upon examining the sources I couldn't pass by the comments you left in the https://github.com/sasa1977/elixir-in-action/blob/master/code_samples/ch07/persistable_todo_cache/lib/todo/cache.ex#L34

Can you please elaborate how does that help with testing? Previously I had a hard time testing GenServer services http://stackoverflow.com/questions/37873855/how-to-avoid-race-condition-during-testing-handle-cast-with-database-query/37875993#37875993 and maybe there your answer can give an insight that will ease that burden.
sjuric (86) [Avatar] Offline
#2
Hi,

I'm glad you're enjoying the book smilie

The :stop trick is used to ensure that the server process is stopped from the test. Usually you don't need to do this. If you start your test processes with start_link, then they will terminate together with the test process. However, in this code I'm using start, so I need to do this manually.

It's worth mentioning that this solution is now obsolete. Since Elixir 1.2 and Erlang 18, there is GenServer.stop available which can be used for this purpose, so no need to handle custom messages.

When it comes to your Stack Overflow question, it's not clear what exactly are you trying to test? Your test issues a cast and then stops. As the answer states, you could ensure that the cast is processed by issuing a synchronous request to the same process. There's no need to introduce special handlers in the server though. Invoking e.g. :sys.get_state after in the test process after you send a message could help, because it will return after the previous message is processed.

However, I'd advise to think about the test purpose. The test in the SO question doesn't seem to test anything. Presumably you somehow want to test the outcome of App.Notifications.Manager.send. If you do that, then your test process can't exit before the cast is handled, because it is awaiting on its result.

How exactly will you verify the output of cast depends on a particular use-case. One solution is to send a notification message to interested parties. In your case, you'd need some kind of mechanism which would allow you to dynamically register your test process with your event manager. Then the test process would receive a notification message, and you could assert on it using assert_receive. I tend to use such approach occasionally, and rely on the gproc lib for dynamic pubsub. I explained it briefly near the end of my talk on discovering processes.

Another approach is to use Erlang tracing capabilities to make the test process receive a message when some function is called and when it returns. I've described this idea here. So in your test process you could setup a trace to the event manager, and assert that you receive a message when a function (e.g. handle_cast) returns. You can also find some related info in this post on elixirforum.com and the subsequent comment by José.
Yevhenii Kurtov (3) [Avatar] Offline
#3
OMG, thank you Saša for such a comprehensive answer!
I think "Reliably tested OTP patterns" can be a topic for your second book, akin to https://pragprog.com/book/nrtest2/rails-4-test-prescriptions