If you're coming to Elixir from another language, you might wonder how to assert that a process sent a message to another process.
The key is to remember that each test itself is also an Erlang process (and has its own pid).
We can send a message to the test process's mailbox, and then use `assert_receive` to assert we got the message.
For example:
test "something" do IO.puts "I am test, my pid is #{inspect self}" send(self, "hello test") assert_receive "hello test" end
When you want to assert that a process sends a message, you just need to make sure the process is sending the messages back to the test's pid.
We do this by creating a stub that forwards messages.
Example
Say you have GenServer process that handles Payroll. Each time an employee is registered with the process, it sends a notification using GenEvent.
You want to assert that the event is sent.
This is what your Payroll server looks like:
defmodule Payroll do use GenServer # convenience function for starting up server, requires an event manager def start_link(manager) do # start GenServer with current module, and pass manager as state GenServer.start_link(__MODULE__, [%{events: manager}]) end # initialize state def init([state]) do {:ok, state} end def handle_call({:add, employee}, _from, state) do # send out event GenEvent.notify(state.events, {:added, employee}) # reply :ok {:reply, :ok, state} end # public API for adding employee def add_employee(pid, employee) do GenServer.call(pid, {:add, employee}) end end
It can be used like this:
# start a gen event server {:ok, events} = GenEvent.start # start the payroll service and pass the event manager's pid {:ok, payroll} = Payroll.start_link(events) # spawn another process that will print out events spawn fn -> GenEvent.stream(events) |> Stream.each(&IO.inspect/1) |> Enum.to_list end # add an employee to the payroll service, which will result in notification being sent to previously spawned process Payroll.add_employee(payroll, %{name: "Josh", salary: 1_000_000})
To test it, we'll need to create a stub GenEvent handler to forward messages back to the test:
# inside payroll_test.exs # define a stub GenEvent handler defmodule Forwarder do use GenEvent # handle all events, first parameter is the event, second if the state def handle_event(event, test_pid) do # send the event to `test_pid` send(test_pid, event) # respond `:ok` and keep the same state (the test's pid) {:ok, test_pid} end end # create all the processes we need in the setup # ExUnit automatically terminates child processes after each test completes setup do # create a GenEvent process {:ok, events} = GenEvent.start # start the payroll process, passing the event process's pid {:ok, payroll} = Payroll.start_link(events) # add the Forwarder as a handler, pass the current pid (self) as the state GenEvent.add_handler(events, Forwarder, self) # return test state {:ok, %{events: events, payroll: payroll}} end test "adding employee, sends notification", state do Payroll.add_employee(state.payroll, %{name: "Josh", salary: 1_000_000}) assert_receive {:added, %{name: "Josh", salary: 1_000_000}} end
And that is all folks..
Happy Elixiring!