Using Process.register in async tests

Is it a good practice to use Process.register in async tests? I’m doing something like this:

Process.register self(), :test

So that I can send messages to :test and assert whether a callback runs:

MyCode.run fn ->
  send :test, :callback_ran
end

assert_receive :callback_ran

I could also grab a reference to self from inside the callback, with the downside of adding more code/noise.

So far I’m getting no errors with Process.register, but I wonder if there are drawbacks?

As soon as you do anything that amounts to using global variables, you should either explicitly specify async: false on the test module or prepare to have really hard to debug sporadic test failures. I love quick test feedback and therefore parallel test suites, so I’ll go through quite some pains to make sure I never need to do global things - it also happens to make your code more robust (what if you pull in a library that registers :test as well?).

In your example,

me = self()
MyCode.run fn ->
   send me, :callback_ran
end
...

should have the same result, for example.

Thanks, @cdegroot. Actually, I was experimenting and tried changing the async test from grabbing a self reference within the closure (as you exemplified) to using Process.register, and was not getting failures as a result of that move. So I thought “is there a chance it works differently than I think it does?” :slight_smile:

But, as you said, this is flaky and I should expect failures sooner or later.

I found a nice alternative when dealing with more than one test:

setup do
  {:ok, self: self()}
end

test "my thing", %{self: me} do
  MyCode.run fn ->
    send me, :callback_ran
  end

  assert_receive :callback_ran
end

This avoids repeating me = self() all over.

1 Like