PostHog.Test (posthog v2.1.0)

View Source

PostHog Test Utilities Module

PostHog makes it simple to test captured events. Make sure to set test_mode in your config/test.exs:

config :posthog, 
  test_mode: true

Now PostHog will not send your captured events to the server and will instead keep them in memory, easily accessible for assertions:

test "capture event" do
  PostHog.capture("event", %{distinct_id: "distinct_id"})

  assert [event] = PostHog.Test.all_captured()

  assert %{
            event: "event",
            distinct_id: "distinct_id",
            properties: %{},
            timestamp: _
          } = event
end

Concurrency

PostHog uses NimbleOwnership ownership mechanism to determine which events belong to which tests. This should work very well for most tests.

However, in some cases captured events cannot be traced back to tests. Those tests are usually well-known and are run with async: false mode. For those, PostHog lets you enable "global" or "shared" mode, where all events captured within the system will be accessible by the test.

To switch PostHog to shared mode, just call PostHog.Test.set_posthog_shared/1 in your setup:

defmodule MyTest do
  use ExUnit.Case, async: false
  
  setup_all {PostHog.Test, :set_posthog_shared}
  
  test "complex test" do
    # run some code
    assert events = PostHog.Test.all_captured()
    assert Enum.any?(events, & match?(%{event: "my event"}, &1))
  end
end

PostHog will revert back from shared mode to private once the test suite or test process exits.

Assertions in shared mode

In shared mode, your test can easily pick up events from background jobs, other tests, or some rogue processes. Design your assertions accordingly to avoid flaky tests!

Summary

Functions

Retrieves all events captured by PostHog.

Allows a process to share captured events with another process.

Sets PostHog to shared (or "global") mode.

Functions

all_captured(name \\ PostHog)

@spec all_captured(PostHog.supervisor_name()) :: [map()]

Retrieves all events captured by PostHog.

In private mode, these are all events that can be attributed to the current process via ownership.

In shared mode, these are all captured events.

allow(name \\ PostHog, owner, pid_to_allow)

@spec allow(PostHog.supervisor_name(), pid(), pid() | (-> resolved_pid)) ::
  :ok | {:error, NimbleOwnership.Error.t()}
when resolved_pid: pid() | [pid()]

Allows a process to share captured events with another process.

Imagine that your test spawns an asynchronous process that captures an event. By default, the test process won't be able to access this event. To mitigate this, the test process should explicitly allow the spawned process' events to appear in its own personal stash of captured events.

test "allowance" do
  test_pid = self()

  pid =
    spawn(fn ->
      receive do
        :go -> :ok
      end

      PostHog.capture("event", "user123")
      send(test_pid, :ready)
    end)

  PostHog.Test.allow(test_pid, pid)

  send(pid, :go)
  assert_receive :ready

  assert [%{event: "event"}] = PostHog.Test.all_captured()
end

Caller Tracking

In practice, explicit allowances are a tool of last resort. Caller tracking allows for automatic ownership propagation. Designing your app with this in mind is the key to painless testing.

set_posthog_shared(test_context \\ %{})

@spec set_posthog_shared(map()) :: :ok

Sets PostHog to shared (or "global") mode.

In this mode, all events captured by all processes are accessible by the test.

Usually used in combination with async: false.

Examples:

setup :set_posthog_shared