# `Ash.Signals.Test`
[🔗](https://github.com/ash-project/ash/blob/v3.36.1/lib/ash/signals/test.ex#L5)

Test helpers for asserting on emitted signals, and for testing listeners.

`assert_emits_signal/4` and `refute_emits_signal/4` check the signals emitted while running a
function, and need no setup:

```elixir
import Ash.Signals.Test

test "creating a post emits post_created" do
  post =
    assert_emits_signal MyApp.Blog.Signals, :post_created, %{title: "Hello"}, fn ->
      MyApp.Blog.create_post!("Hello")
    end

  assert post.title == "Hello"
end
```

`assert_signal_emitted/4` and `refute_signal_emitted/4` check the signals emitted so far, and
require calling `capture_signals/1` first:

```elixir
setup :capture_signals

test "creating a post emits post_created" do
  %{id: post_id} = MyApp.Blog.create_post!("Hello")

  assert_signal_emitted MyApp.Blog.Signals, :post_created, %{post_id: ^post_id}
end
```

Only signals emitted by the test process, or by processes it started (that have it in their
`$callers`, like tasks and LiveViews under test), are seen. No other process is sent anything,
and listeners still run as usual.

Patterns match against the signal struct, and may use pins or bind variables.

To test listeners on their own, `react_to_signal/4` delivers a signal to them as if it had been
emitted:

```elixir
test "placing an order awards points" do
  :ok =
    react_to_signal(
      MyApp.Shop.Signals,
      :order_placed,
      %{order_id: order.id, customer_id: customer.id},
      only: MyApp.Loyalty.Account
    )

  assert MyApp.Loyalty.points!(customer) == 100
end
```

To also match on who emitted a signal, receive the messages sent after `capture_signals/1`
directly. They have the shape `{:ash_signal_emitted, signal_module, signal_name, signal,
metadata}`, where `metadata` is the metadata of the `[:ash, :signal, :emitted]` telemetry
event, see `Ash.Signals`.

# `assert_emits_signal`
*macro* 

Runs the function, asserts that it emitted a signal matching the given pattern, and returns
the function's result. Variables bound by the pattern are available afterwards.

```elixir
assert_emits_signal MyApp.Blog.Signals, :post_created, fn ->
  MyApp.Blog.create_post!("Hello")
end

post =
  assert_emits_signal MyApp.Blog.Signals, :post_created, %{post_id: post_id}, fn ->
    MyApp.Blog.create_post!("Hello")
  end

assert post.id == post_id
```

# `assert_signal_emitted`
*macro* 

Asserts that a signal matching the given pattern was emitted since `capture_signals/1` was
called, and returns the signal.

```elixir
assert_signal_emitted MyApp.Blog.Signals, :post_created
assert_signal_emitted MyApp.Blog.Signals, :post_created, %{title: "Hello"}

%{post_id: post_id} = assert_signal_emitted MyApp.Blog.Signals, :post_created
```

The optional timeout, defaulting to `0`, waits for signals emitted by other processes.

# `capture_signals`

```elixir
@spec capture_signals(map()) :: :ok
```

Sends signals emitted by the current test, or by processes it started, to the test process,
for `assert_signal_emitted/4` and `refute_signal_emitted/4`.

Can be used as `setup :capture_signals`. Stops when the test exits.

# `react_to_signal`

```elixir
@spec react_to_signal(
  Ash.Signals.t(),
  atom(),
  map() | Keyword.t() | [map()],
  Keyword.t()
) ::
  :ok | {:error, term()}
```

Delivers a signal to its listeners as if it had been emitted with the given payload, so
listeners can be tested without the action that emits it. Pass a list of payloads to deliver
signals emitted together, like a bulk action does.

Each listener receives the signal through its own delivery, as it would if the signal had been
emitted, so a listener that runs in a job only enqueues it. Nothing is emitted, so the signal
isn't seen by the assertions above.

Returns `:ok`, or the error of the first delivery that fails.

## Options

* `:only` - Only delivers to the listeners on these resources, or to these `{resource,
  listener_name}` pairs. Raises if no listener matches.
* `:inline?` - Runs every listener immediately, whatever its delivery. Defaults to `false`.
* `:actor`, `:tenant`, `:authorize?`, `:tracer`, `:context` and `:scope` - What listeners run
  with, as for `Ash.Signals.emit/5`.

```elixir
react_to_signal(
  MyApp.Shop.Signals,
  :order_placed,
  %{order_id: order_id, customer_id: customer_id},
  only: {MyApp.Loyalty.Account, :award_points},
  actor: customer
)
```

# `refute_emits_signal`
*macro* 

Runs the function, asserts that it did not emit a signal matching the given pattern, and
returns the function's result.

```elixir
refute_emits_signal MyApp.Blog.Signals, :post_created, fn ->
  MyApp.Blog.create_draft!("Hello")
end
```

# `refute_signal_emitted`
*macro* 

Asserts that no signal matching the given pattern was emitted since `capture_signals/1` was
called.

```elixir
refute_signal_emitted MyApp.Blog.Signals, :post_created
refute_signal_emitted MyApp.Blog.Signals, :post_created, %{title: "Draft"}
```

The optional timeout, defaulting to `0`, waits for signals emitted by other processes.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
