Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Recipe] Testing read model projectors #18

Open
slashdotdash opened this issue Nov 5, 2020 · 1 comment
Open

[Recipe] Testing read model projectors #18

slashdotdash opened this issue Nov 5, 2020 · 1 comment

Comments

@slashdotdash
Copy link
Member

slashdotdash commented Nov 5, 2020

How can I test a read model projector so that tests wait until events have been projected?

Using Elixir telemetry and the after_update/3 callback function

Commanded Ecto projections provides an after_update/3 callback function. This gets called after each event is projected. You can use the funtion to publish a notification whenever an event is projected, including the database changes.

Here’s an example using the Elixir/Erlang telemetry library.

In the projector:

def after_update(event, metadata, changes) do
  :telemetry.execute(
    [:projector],
    %{system_time: System.system_time()},
    %{event: event, metadata: metadata, changes: changes, projector: __MODULE__}
  )
end

In a test:

setup do
  reply_to = self()
  :ok =
    :telemetry.attach(
      "test-handler",
      [:projector],
      fn event, measurements, metadata, reply_to ->
        send(reply_to, {:telemetry, event, measurements, metadata})
      end,
      self()
    )
end

Usage in test to wait:

assert_receive {:telemetry, [:projector], _measurements, %{event: event, metadata: metadata, changes: changes}}

This approach will ensure the test blocks until the projector has done its work and then you can run either use the changes from the Ecto.Multi or run a query to verify the database changes. You can also use pattern matching on the event from the telemetry metadata to wait for a particular event type.

The performance impact at runtime is negligible if there are no handlers attached to a telemetry event.

@satom99
Copy link

satom99 commented Feb 1, 2024

Commanded Ecto projectors are regular Commanded.Event.Handlers under the hood.

Recipe #15 explains how event handlers can be tested, and includes the following example:

    # Or call handler directly with an event (doesn’t require the handler process to be started)
    :ok = CustomerPromotionHandler.handle(event, metadata)

Which shows that event handlers can be tested by calling the handle/2 callback directly. Rather than dispatching an event.

Now take one of the examples in the commanded-ecto-projection documentation. We could do the following in tests:

      metadata = %{
        event_number: 1,
        handler_name: "fake-handler-name"
      }

      :ok = MyApp.ExampleProjector.handle(event, metadata)

So we can test Commanded Ecto projections directly by calling the handle/2 function on the projector. There is no need to depend on command dispatches, nor telemetry events to wait and acknowledge that the projector was called. We can simply build an event, call the projector, and that's about it.

@slashdotdash Wdyt? Would this be a recommended approach?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants