Exceptions from external code (send email using javax.mail)

175 views
Skip to first unread message

Jop van Raaij

unread,
Sep 3, 2013, 3:35:28 AM9/3/13
to clean-code...@googlegroups.com
I'm writing some software to send e-mails using TDD. I start from a UseCase class which will send the e-mails. Because I want to test the logic, I first implement sending e-mails using a memory-based e-mail sender EmailSenderMemory. When logic is working as expected, I inject an e-mail sender using javax.mail. The class EmailSenderJavaxMail implements EmailSender, which implements the method sendEmail(), could look something like this:

try {
   
Message msg = new MimeMessage(session);
    msg
.setFrom(new InternetAddress("ad...@example.com", "Example.com Admin"));
    msg
.addRecipient(Message.RecipientType.TO, new InternetAddress("us...@example.com", "Mr. User"));
    msg
.setSubject("Your Example.com account has been activated");
    msg
.setText(msgBody);
   
Transport.send(msg);
} catch (AddressException e) {
   
// ...
} catch (MessagingException e) {
   
// ...
}

Now I suddenly have two Exceptions I have to deal with. I don't like those exceptions to leak into my application, which means I have to catch and handle them here. I did never write any logic in my TDD cycles to cause these error situations, so the catches will not be covered by my tests. Is this wrong? Validation for the e-mailaddress (causing the AddressException) is already implemented in my application before starting the send use-case of the e-mail. The AddressException therefore should not be able to occur. It feels dangerous not to do anything with the exceptions, however.
When doing the extra effort to force the exceptions to happen from the tests and check the exceptions occur makes my development ECDT (External Code Driven Tests).

Love to hear suggestions.

featurefabrik

unread,
Sep 4, 2013, 8:29:55 AM9/4/13
to clean-code...@googlegroups.com
Hi Jop!

I'm totally with you, that those javax.mail-specific exceptions shouldn't be found in your core business logic. It would create a dependency in the wrong direction.

But these errors are somehow semantically relevant to your application. I would build up specific exception-classes for the possible errors. These exceptions should be as generic as your EmailSender interface.

From this starting point, you can handle your generic exception without pain in the usecase, and inject special test doubles in your error tests that only throw these kinds of exceptions.

Your actual implementations can then catch their own framework-specific errors internally and throw the semantical exception which your usecase is capable of dealing with.

Greetings!

Uncle Bob

unread,
Sep 4, 2013, 12:15:22 PM9/4/13
to clean-code...@googlegroups.com
I'm with featurefabrik on this one.  I'll add that you can likely bury the javax exceptions in side your specific exceptions so that when you find yourself debugging, you'll have the stack traces you need.

Jop van Raaij

unread,
Sep 10, 2013, 2:23:29 AM9/10/13
to clean-code...@googlegroups.com
Thank you for the suggestions, I agree.

I do feel a bit confused about adding code which is not driven by my tests. The only way I can think of to 'approve' this external force which is writing tests and code, is by accepting the guys who build javax.mail did a good job and have dealt with more error/exception situation to handle as I've done until now. Eventually I probably come to the same solution as they did, but it'll cost me a lot of time and effort.

Sebastian Gozin

unread,
Sep 10, 2013, 4:38:23 AM9/10/13
to clean-code...@googlegroups.com
It should be possible to handle those exceptions in an integration test no?

When you TDD your usecase you also learn about the shape of your EmailSender collaborator and it's expected behavior. You can use this information to assemble a contract test suite all implementations of EmailSender should pass. This allows you to inform would be implementors of the behavior that is expected from them.

So, now you can write a JavaxEmailSender implementation and integration test which is backed by something like Dumbster to trigger the javax exceptions and map them to your own exception classes.

Sebastian Gozin

unread,
Sep 10, 2013, 4:44:13 AM9/10/13
to clean-code...@googlegroups.com
Forgive me I seem to have glossed over the fact that you did not anticipate the edge case in your usecase but discovered it while dealing with an external library which did.
Is this a bad way to be informed of such a thing though?

Jop van Raaij

unread,
Sep 10, 2013, 10:06:49 AM9/10/13
to clean-code...@googlegroups.com
Good question :). No, I think it is not. I guess it is like someone else asking the question 'did you think about testing this edge case?', which would lead to another test and code to make the test pass.

I'm a bit afraid I'll go back to code first and then (maybe) write the test. Sometimes it is more easy to just write code, because thinking about and writing the test costs too much effort. Because the code (Exceptions) are already there and I'm writing the test after the code had been written, it feels like the wrong order. But I now see the external code can be seen as a source of suggestions what to test for, like a person could be.

Sebastian Gozin

unread,
Sep 10, 2013, 3:27:37 PM9/10/13
to clean-code...@googlegroups.com
I'm not sure code first is really what makes things easier here. Perhaps you just feel more comfortable thinking about e-mail?

I mean, from the point of view of the usecase are we talking about e-mail or about asking someone a question and does e-mail just happen to be the channel by which we transport this question to him? Along that line, does it make sense for the exception to reach our use case at all? Some perhaps do, but I can easily image a bunch of the javax.mail exceptions are wholly uninteresting from a usecase point of view and simply need to be escalated and handled on a technical/transport level. Encountered a failure? Ok, don't bother the user but page an engineer instead.

If you bind your usecase to e-mail you have open closed issues.

Carlos Ble

unread,
Sep 11, 2013, 6:30:38 AM9/11/13
to clean-code...@googlegroups.com
Hi,
The benefit from writing tests first is that you get the chance to think of the design. You choose a design and then implement it the best way you know. 
However, when using third party tools the design is already done and you don't have margin to make design decisions. In this case I do write the tests after.
You can use a "fake" object for "Transport.send" and test everything else with an integration test.

Cheers :-)

Caio Fernando Bertoldi Paes de Andrade

unread,
Sep 11, 2013, 8:16:38 AM9/11/13
to clean-code...@googlegroups.com
Hi guys,

Testing third-parties can be done very healthily via learning tests. I searched like crazy here but I couldn’t find where I read about this. It basically consists of writing key tests for a library while you learn how it works. This way you make sure the library works as you expect and when a new version is available, you just run your learning tests to check if it’s still compatible.

About the implications a third-party library can have in your application’s design: Just write an adapter for it, exposing only the usage (and therefore the design) that makes sense for your application. The third-party will then turn into a plugin. Yes, the adapter will have to deal with stuff (e.g. exceptions) you don’t want to, but the adapter is an implementation detail outside of your application design. The interface (e.g. EmailGateway) is what has to be very clean.

Hope this helps,
Caio

--
The only way to go fast is to go well.
---
You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discu...@googlegroups.com.
To post to this group, send email to clean-code...@googlegroups.com.
Visit this group at http://groups.google.com/group/clean-code-discussion.

Juan Diego Hereñú

unread,
Sep 11, 2013, 8:23:21 AM9/11/13
to clean-code...@googlegroups.com
Roy Osherove always says that the best way to learn a third-party library is by writing tests to it.

Juan



2013/9/11 Caio Fernando Bertoldi Paes de Andrade <caio...@gmail.com>

Sebastian Gozin

unread,
Sep 12, 2013, 5:25:51 AM9/12/13
to clean-code...@googlegroups.com
I have done this for Apache Shiro and I deeply recommend this approach. It turns magic into oh so this is what those guys were thinking when they wrote this?


On Wednesday, September 11, 2013 2:23:21 PM UTC+2, Juan Diego Hereñú wrote:
Roy Osherove always says that the best way to learn a third-party library is by writing tests to it.

Juan

2013/9/11 Caio Fernando Bertoldi Paes de Andrade <caio...@gmail.com>
Hi guys,

Testing third-parties can be done very healthily via learning tests. I searched like crazy here but I couldn’t find where I read about this. It basically consists of writing key tests for a library while you learn how it works. This way you make sure the library works as you expect and when a new version is available, you just run your learning tests to check if it’s still compatible.

About the implications a third-party library can have in your application’s design: Just write an adapter for it, exposing only the usage (and therefore the design) that makes sense for your application. The third-party will then turn into a plugin. Yes, the adapter will have to deal with stuff (e.g. exceptions) you don’t want to, but the adapter is an implementation detail outside of your application design. The interface (e.g. EmailGateway) is what has to be very clean.

Hope this helps,
Caio

On 11 Sep 2013, at 07:30, Carlos Ble <ble.j...@gmail.com> wrote:

Hi,
The benefit from writing tests first is that you get the chance to think of the design. You choose a design and then implement it the best way you know. 
However, when using third party tools the design is already done and you don't have margin to make design decisions. In this case I do write the tests after.
You can use a "fake" object for "Transport.send" and test everything else with an integration test.

Cheers :-)


El martes, 10 de septiembre de 2013 07:23:29 UTC+1, Jop van Raaij escribió:
Thank you for the suggestions, I agree.

I do feel a bit confused about adding code which is not driven by my tests. The only way I can think of to 'approve' this external force which is writing tests and code, is by accepting the guys who build javax.mail did a good job and have dealt with more error/exception situation to handle as I've done until now. Eventually I probably come to the same solution as they did, but it'll cost me a lot of time and effort.


On Wednesday, September 4, 2013 6:15:22 PM UTC+2, Uncle Bob wrote:
I'm with featurefabrik on this one.  I'll add that you can likely bury the javax exceptions in side your specific exceptions so that when you find yourself debugging, you'll have the stack traces you need.

--
The only way to go fast is to go well.
---
You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discussion+unsub...@googlegroups.com.

To post to this group, send email to clean-code...@googlegroups.com.
Visit this group at http://groups.google.com/group/clean-code-discussion.

--
The only way to go fast is to go well.
---
You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discussion+unsub...@googlegroups.com.

Jop van Raaij

unread,
Sep 12, 2013, 7:30:56 AM9/12/13
to clean-code...@googlegroups.com
I think Uncle Bob does this in one of his books. I think it was Clean Code. He was exploring a logging library or something like that.

My point was not about writing tests to learn third-party libraries, but about the influence of those libraries on my code. They force you to do things, like handling their exceptions, instead of the tests you have. The change (of behavior) in my application is therefore changed by something other then a test from a user story scenario/usecase. This troubled me. But no more, as I agree on the conclusion the third party code can be seen as a comment on my applications implementation of certain functionality. This provides a new insight of the behavior of my application, new tests, and new code to cope with the new behavior. I just forgot an edge case and did not found it testing the application. The third-party library pointed me to the edge-case.
Wrapping the external Exceptions into Exceptions of my own domain is the natural way to go. Not doing so means dependencies on the external library/behavior, which we want to avoid at all times.
Reply all
Reply to author
Forward
0 new messages