Testing try rescue errors

TLDR; I’ve implemented a try catch block in a function and I’m wondering the best way to test the rescue statement. Specifically, I’m wondering what the best way to initially raise the error is, and then how I can test that it has been handled.


Longer version:
A few weeks ago I posted an issue I was having with bamboo_stmp. I’ve been unable to resolve it but I figure I can handle things better by rescuing the Bamboo.STMPAdapter.STMPError.

I’ve created a little rescue:

try do 
  email 
  |> deliver_now 

rescue 
  error in Bamboo.STMPAdapter.STMPError -> 
    case error.raw do
      {:no_more_hosts, err} ->
       handle_error(err)
    ...
   end 
end 

I believe this works but I’m not sure how I can validate that it’s working since it’s an external dependency that raises the error.
My hacky approach is to include something like:

def send_customer_email(email, opts) do 
try do 
  raise_errors(Keyword.get(opts, :raise, false))
  email 
  |> deliver_now 
...
end

defp raise_errors(true) do 
  raise %Bamboo.SMTPAdapter.SMTPError{
      message: "problem"}
end  

This, admittedly, feels like a smell because I’m including some logic just for tests in my production code, if anyone has another suggestion I’m all ears here.
Another problem is that the rescue statement dosen’t seem to actually handle things, running the test including a raise: true in the opts I get this error:

test send_customer_email/1 Fails with grace (Customers.SendEmailTest)
     ** (Bamboo.SMTPAdapter.SMTPError) problem

But if I run the test with:

      assert_raise Bamboo.SMTPAdapter.SMTPError, fn ->
        exec(ctxt)
      end

everything passes. I don’t want to assert_raise I want to use the rescue to handle whatever error is raised so the test proceeds as if nothing was raised.

I think a much cleaner approach would be to introduce a behaviour and set up expectations on it using Mox. Alternatively, you can pass the module as a default arg:

def send_cutomer_email(email, mod \\ __MODULE__) do
  # ...
  mod.deliver_now(email)
  # ...
end
3 Likes

SMTP has been transposed to STMP here. Note that Bamboo.STMPAdapter.STMPError is a completely valid atom, just not one we care about

See catch error · Issue #50 · fewlinesco/bamboo_smtp · GitHub for an example of handling errors like this.

1 Like

@stefanchrobot thanks for the response, sorry I didn’t get on this sooner. I ended up implementing Mox as you recommended. I’ve never worked with Mox before so it took a bit of effort to get it setup but once I did it solved the issue in a really concise way.

@al2o3cr thanks for the tip. This is likely why I was unable to catch the error in the first place in my tests.

End of story: PR approved, deployed, and no more failed emails!

2 Likes