rspec-notifications
RSpec matchers for testing ActiveSupport::Notifications.
require "rspec/notifications"
expect {
service.call
}.to emit_notification("user.created")
The matcher subscribes to ActiveSupport::Notifications before running the
block and unsubscribes afterward, so there is nothing to set up or tear down.
Payloads
Match the payload with .with. Matching is partial — only the keys you name
are checked, so extra payload keys are ignored.
expect {
service.call
}.to emit_notification("user.created").with(user_id: user.id)
Values support embedded RSpec matchers, nested hashes, and any composable matcher:
expect {
service.call
}.to emit_notification("user.created").with(user_id: kind_of(Integer))
# nested hashes match exactly; use a_hash_including for a nested subset
expect {
service.call
}.to emit_notification("user.created").with(user: a_hash_including(role: "admin"))
Counts
expect { service.call }.to emit_notification("user.created").once
expect { service.call }.to emit_notification("user.created").twice
expect { service.call }.to emit_notification("user.created").exactly(3).times
expect { service.call }.to emit_notification("user.created").at_least(2).times
expect { service.call }.to emit_notification("user.created").at_most(2).times
Without a count, the matcher passes when the notification is emitted at least once. Counts apply to events matching both the name and the payload.
Names
Names can be matched exactly, with a * wildcard, or with a Regexp:
emit_notification("user.created") # exact
emit_notification("user.*") # wildcard
emit_notification(/user\./) # regexp
Negation
expect { service.call }.not_to emit_notification("user.created")
Aliases
instrument_notification is an alias for emit_notification.
Edge cases
- Nested notifications — each instrumented event is captured independently, including notifications instrumented within the block of another.
- Exceptions — if the block raises, the exception propagates (the matcher
still unsubscribes). When
ActiveSupport::Notifications.instrumentre-raises, it adds:exception/:exception_objectto the payload, which you can match on with.with. - Concurrency — notifications delivered from other threads during the block are captured too; appends are guarded by a mutex.
Contributing
Yes please :)
- Fork it
- Create your feature branch (
git checkout -b my-feature) - Ensure the tests pass (
bundle exec rspec) - Commit your changes (
git commit -am 'awesome new feature') - Push your branch (
git push origin my-feature) - Create a Pull Request