Skip to content

Commit 547e4af

Browse files
author
Joel Lubrano
committed
Add exactly, at_least, at_most functionality for have_enqueued_mail matcher.
1 parent ab4a6dc commit 547e4af

File tree

2 files changed

+100
-12
lines changed

2 files changed

+100
-12
lines changed

lib/rspec/rails/matchers/have_enqueued_mail.rb

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ def initialize(mailer_class, method_name)
1313
@mailer_class = mailer_class
1414
@method_name = method_name
1515
@args = []
16+
@job_matcher = ActiveJob::HaveEnqueuedJob.new(ActionMailer::DeliveryJob)
17+
set_expected_count(:exactly, 1)
1618
end
1719

1820
def description
@@ -23,18 +25,33 @@ def matches?(block)
2325
raise ArgumentError, 'have_enqueued_mail and enqueue_mail only work with block arguments' unless block.respond_to?(:call)
2426
check_active_job_adapter
2527

26-
job_matcher = ActiveJob::HaveEnqueuedJob.new(ActionMailer::DeliveryJob)
27-
job_matcher.with(*mailer_args)
28-
job_matcher.matches?(block)
28+
@job_matcher.with(*mailer_args)
29+
@job_matcher.matches?(block)
2930
end
3031

3132
def with(*args)
3233
@args = args
3334
self
3435
end
3536

37+
%i[exactly at_least at_most].each do |method|
38+
define_method(method) do |count|
39+
@job_matcher.public_send(method, count)
40+
set_expected_count(method, count)
41+
self
42+
end
43+
end
44+
45+
%i[once twice thrice].each do |method|
46+
define_method(method) do
47+
@job_matcher.public_send(method)
48+
exactly(method)
49+
end
50+
end
51+
3652
def failure_message
3753
base_message.tap do |msg|
54+
msg << " #{expected_count_message}"
3855
msg << " with #{@args}" if @args.any?
3956
end
4057
end
@@ -49,6 +66,10 @@ def base_message
4966
"expected to enqueue #{@mailer_class.name}.#{@method_name}"
5067
end
5168

69+
def expected_count_message
70+
"#{@expected_count_type.to_s.tr('_', ' ')} #{@expected_count} #{@expected_count == 1 ? 'time' : 'times'}"
71+
end
72+
5273
def mailer_args
5374
[@mailer_class.name, @method_name.to_s, 'deliver_now'] + @args
5475
end
@@ -57,6 +78,16 @@ def check_active_job_adapter
5778
return if ::ActiveJob::QueueAdapters::TestAdapter === ::ActiveJob::Base.queue_adapter
5879
raise StandardError, "To use HaveEnqueuedMail matcher set `ActiveJob::Base.queue_adapter = :test`"
5980
end
81+
82+
def set_expected_count(relativity, count)
83+
@expected_count_type = relativity
84+
@expected_count = case count
85+
when :once then 1
86+
when :twice then 2
87+
when :thrice then 3
88+
else Integer(count)
89+
end
90+
end
6091
end
6192

6293
# @api public

spec/rspec/rails/matchers/have_enqueued_mail_spec.rb

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ def email_with_args(arg1, arg2); end
1616
ActiveJob::Base.queue_adapter = :test
1717
end
1818

19+
around do |example|
20+
original_logger = ActiveJob::Base.logger
21+
ActiveJob::Base.logger = Logger.new(nil) # Silence messages "[ActiveJob] Enqueued ...".
22+
example.run
23+
ActiveJob::Base.logger = original_logger
24+
end
25+
1926
describe "have_enqueued_mail" do
2027
it "passes when a mailer method is called with deliver_later" do
2128
expect {
@@ -42,16 +49,66 @@ def email_with_args(arg1, arg2); end
4249
end
4350

4451
it "passes when negated" do
45-
expect { }.not_to have_enqueued_email(TestMailer, :test_email)
52+
expect { }.not_to have_enqueued_mail(TestMailer, :test_email)
53+
end
54+
55+
it "counts only emails enqueued in the block" do
56+
TestMailer.test_email.deliver_later
57+
58+
expect {
59+
TestMailer.test_email.deliver_later
60+
}.to have_enqueued_mail(TestMailer, :test_email).once
4661
end
4762

48-
# it "counts only emails enqueued in the block" do
49-
# TestMailer.test_email
63+
it "fails when too many emails are enqueued" do
64+
expect {
65+
expect {
66+
TestMailer.test_email.deliver_later
67+
TestMailer.test_email.deliver_later
68+
}.to have_enqueued_mail(TestMailer, :test_email).exactly(1)
69+
}.to raise_error(/expected to enqueue TestMailer.test_email exactly 1 time/)
70+
end
5071

51-
# expect {
52-
# TestMailer.test_email
53-
# }.to have_enqueued_email(TestMailer, :test_email).once
54-
# end
72+
it "passes with :once count" do
73+
expect {
74+
TestMailer.test_email.deliver_later
75+
}.to have_enqueued_mail(TestMailer, :test_email).once
76+
end
77+
78+
it "passes with :twice count" do
79+
expect {
80+
TestMailer.test_email.deliver_later
81+
TestMailer.test_email.deliver_later
82+
}.to have_enqueued_mail(TestMailer, :test_email).twice
83+
end
84+
85+
it "passes with :thrice count" do
86+
expect {
87+
TestMailer.test_email.deliver_later
88+
TestMailer.test_email.deliver_later
89+
TestMailer.test_email.deliver_later
90+
}.to have_enqueued_mail(TestMailer, :test_email).thrice
91+
end
92+
93+
it "matches based on mailer class and method name" do
94+
expect {
95+
TestMailer.test_email.deliver_later
96+
TestMailer.email_with_args(1, 2).deliver_later
97+
}.to have_enqueued_mail(TestMailer, :test_email).once
98+
end
99+
100+
it "passes with multiple emails" do
101+
expect {
102+
TestMailer.test_email.deliver_later
103+
TestMailer.email_with_args(1, 2).deliver_later
104+
}.to have_enqueued_mail(TestMailer, :test_email).and have_enqueued_mail(TestMailer, :email_with_args)
105+
end
106+
107+
it "passes for mailer methods that accept arguments when the provided argument matcher is not used" do
108+
expect {
109+
TestMailer.email_with_args(1, 2).deliver_later
110+
}.to have_enqueued_mail(TestMailer, :email_with_args)
111+
end
55112

56113
it "passes with provided argument matchers" do
57114
expect {
@@ -72,15 +129,15 @@ def email_with_args(arg1, arg2); end
72129
it "generates a failure message with arguments" do
73130
expect {
74131
expect { }.to have_enqueued_email(TestMailer, :email_with_args).with(1, 2)
75-
}.to raise_error(/expected to enqueue TestMailer.email_with_args with \[1, 2\]/)
132+
}.to raise_error(/expected to enqueue TestMailer.email_with_args exactly 1 time with \[1, 2\]/)
76133
end
77134

78135
it "throws descriptive error when no test adapter set" do
79136
queue_adapter = ActiveJob::Base.queue_adapter
80137
ActiveJob::Base.queue_adapter = :inline
81138

82139
expect {
83-
expect { TestMailer.test_email }.to have_enqueued_mail(TestMailer, :test_email)
140+
expect { TestMailer.test_email.deliver_later }.to have_enqueued_mail(TestMailer, :test_email)
84141
}.to raise_error("To use ActiveJob matchers set `ActiveJob::Base.queue_adapter = :test`")
85142

86143
ActiveJob::Base.queue_adapter = queue_adapter

0 commit comments

Comments
 (0)