diff --git a/lib/bamboo/test.ex b/lib/bamboo/test.ex index a8319804..121cdaa4 100644 --- a/lib/bamboo/test.ex +++ b/lib/bamboo/test.ex @@ -1,6 +1,4 @@ defmodule Bamboo.Test do - @timeout 100 - import ExUnit.Assertions @moduledoc """ @@ -159,33 +157,46 @@ defmodule Bamboo.Test do unsent_email = Bamboo.Email.new_email(subject: "something else") assert_delivered_email(unsent_email) # Will fail """ - def assert_delivered_email(%Bamboo.Email{} = email) do - email = normalize_for_testing(email) - do_assert_delivered_email(email) + defmacro assert_delivered_email(email) do + quote do + import ExUnit.Assertions + email = Bamboo.Test.normalize_for_testing(unquote(email)) + assert_receive({:delivered_email, ^email}, 100, Bamboo.Test.flunk_with_email_list(email)) + end end - defp do_assert_delivered_email(email) do - receive do - {:delivered_email, ^email} -> true - after - @timeout -> flunk_with_email_list(email) + @doc """ + Check whether an email's params are equal to the ones provided. + + Must be used with the `Bamboo.TestAdapter` or this will never pass. In case you + are delivering from another process, the assertion waits up to 100ms before + failing. Typically if an email is successfully delivered the assertion will + pass instantly, so test suites will remain fast. + + ## Examples + + email = Bamboo.Email.new_email(subject: "something") + email |> MyApp.Mailer.deliver + assert_delivered_with(subject: "something") # Will pass + + unsent_email = Bamboo.Email.new_email(subject: "something else") + assert_delivered_with(subject: "something else") # Will fail + """ + defmacro assert_delivered_with(email_params) do + quote bind_quoted: [email_params: email_params] do + import ExUnit.Assertions + assert_receive({:delivered_email, email}, 100, Bamboo.Test.flunk_no_emails_received) + + recieved_email_params = email |> Map.from_struct + assert Enum.all?(email_params, fn({k, v}) -> recieved_email_params[k] == v end), + Bamboo.Test.flunk_attributes_do_not_match(email_params, recieved_email_params) end end - defp flunk_with_email_list(email) do + @doc false + def flunk_with_email_list(email) do if Enum.empty?(delivered_emails) do - flunk """ - There were 0 emails delivered to this process. - - If you expected an email to be sent, try these ideas: - - 1) Make sure you call deliver_now/1 or deliver_later/1 to deliver the email - 2) Make sure you are using the Bamboo.TestAdapter - 3) Use shared mode with Bamboo.Test. This will allow Bamboo.Test - to work across processes: use Bamboo.Test, shared: :true - 4) If you are writing an acceptance test through a headless browser, use - shared mode as described in option 3. - """ + flunk_no_emails_received else flunk """ There were no matching emails. @@ -201,6 +212,37 @@ defmodule Bamboo.Test do end end + @doc false + def flunk_no_emails_received do + flunk """ + There were 0 emails delivered to this process. + + If you expected an email to be sent, try these ideas: + + 1) Make sure you call deliver_now/1 or deliver_later/1 to deliver the email + 2) Make sure you are using the Bamboo.TestAdapter + 3) Use shared mode with Bamboo.Test. This will allow Bamboo.Test + to work across processes: use Bamboo.Test, shared: :true + 4) If you are writing an acceptance test through a headless browser, use + shared mode as described in option 3. + """ + end + + @doc false + def flunk_attributes_do_not_match(params_given, params_received) do + """ + The parameters given do not match. + + Parameters given: + + #{inspect params_given} + + Email recieved: + + #{inspect params_received} + """ + end + defp delivered_emails do {:messages, messages} = Process.info(self, :messages) @@ -310,7 +352,8 @@ defmodule Bamboo.Test do !!Application.get_env(:bamboo, :shared_test_process) end - defp normalize_for_testing(email) do + @doc false + def normalize_for_testing(email) do email |> Bamboo.Mailer.normalize_addresses |> Bamboo.TestAdapter.clean_assigns diff --git a/test/lib/bamboo/adapters/test_adapter_test.exs b/test/lib/bamboo/adapters/test_adapter_test.exs index fedf307d..cc19b7b2 100644 --- a/test/lib/bamboo/adapters/test_adapter_test.exs +++ b/test/lib/bamboo/adapters/test_adapter_test.exs @@ -51,6 +51,14 @@ defmodule Bamboo.TestAdapterTest do assert_raise ExUnit.AssertionError, fn -> assert_delivered_email %{sent_email | to: "oops"} end + + sent_email |> TestMailer.deliver_now + assert_delivered_with(from: {nil, "foo@bar.com"}) + + sent_email |> TestMailer.deliver_now + assert_raise ExUnit.AssertionError, fn -> + assert_delivered_with(from: "oops") + end end test "assert_delivered_email with no delivered emails" do @@ -106,6 +114,33 @@ defmodule Bamboo.TestAdapterTest do end end + test "assert_delivered_with with no delivered emails" do + try do + assert_delivered_with from: {nil, "foo@bar.com"} + rescue + error in [ExUnit.AssertionError] -> + assert error.message =~ "0 emails delivered" + else + _ -> flunk "assert_delivered_email should have failed" + end + end + + test "assert_delivered_with shows non-matching delivered email" do + sent_email = new_email(from: "foo@bar.com", to: ["foo@bar.com"]) + + sent_email |> TestMailer.deliver_now + + try do + assert_delivered_with to: "oops" + rescue + error in [ExUnit.AssertionError] -> + assert error.message =~ "do not match" + assert error.message =~ sent_email.from + else + _ -> flunk "assert_delivered_email should have failed" + end + end + test "assert_no_emails_delivered shows the delivered email" do sent_email = new_email(from: "foo@bar.com", to: ["foo@bar.com"])