views:

797

answers:

5

I want to run unit tests (Junit) on some model classes that typically will send emails confirming that things happened. Is there a mock email server that you can use with unit tests that will let you confirmation that your run tried to send an email without actually sending the emails out?

This seems like it would be a nice to have, just not sure I want to write my own. The app stack for the emailing aspect is Velocity+Spring, so it would be preferable if the test server can be pointed at by simply changing the applicationContext.xml file.

A: 

Phil Haack has a blog post about unit testing email sending, with a solution he coded around a freeware mail server.

Colin Pickard
+6  A: 

I think the Mock JavaMail project is what you want.

JacobM
Absolutely - never mock out a 3rd party interface yourself, it's rarely worth the time. (if you DID want to - use PowerMock). But in this case, just drop Mock JavaMail in place, and magically all your messages will be held in a mock Mailbox for you to examine and assert over. Simple and effective (just keep it out of your production classpath !)
Phantomwhale
A: 

My solution was to wrap the mail server in a class which takes all the config options and has a send() method. In my tests, I'd mock this class and override send() with something that saves the current parameters for the assert.

To test that the mail service itself works, send yourself a mail locally. Try hMail if you're on Windows.

Aaron Digulla
I develop on Windows, but I run the continuous integration server on Ubuntu, so that won't work.
stevedbrown
Sure; use the address "localhost" or "127.0.0.1" and add the same account on Ubunutu for the mail as on Windows. I'm not sure what Ubuntu uses but it already has a mail server installed. Probably qmail or postfix.
Aaron Digulla
+10  A: 

Alternative answer: Dumbster is a fake SMTP server designed for testing against. It's written in Java.

Colin Pickard
+1 for this approach, although SubEthaSMTP might be easier to work with than Dumbster.
matt b
This does exactly what I asked for. I am going to use it for now, I'll probably switch to using a mock object at some point to speed the test up.
stevedbrown
Just a note - running with Dumbster, my unit test is taking about 500ms to startup the server, send an email, and test that the email was sent. This is significantly better than my Jersey framework tests which take ages.
stevedbrown
+2  A: 

I assume you're using Javamail and the problem is that javax.mail.Session is final and therefore can't be mocked.

Seems like others have suggested you simply define your own 'mail session' interface and create an implementation that uses Javamail. In your tests you then simply inject a mock while in 'real-world-mode' you inject the Javamail implementation.

Both JMock and EasyMock will support all the assertions you might want to make on the message you are sending and you testing is complete.

As an aside, I generally try to avoid any out-of-process calls from within unit tests - it kills you when you're running the test suite frequently, which normally translates into it being run less and that's code base issues start to occur.

Nick Holt
That's really a good comment and made me think about my approach some more. I was thinking to spawn a process, but more likely, I should be instantiating a bean in my spring config xml that mocks an email server.
stevedbrown
Hi Steve - that's exactly the point. If you've got another process running as your end-point all of a sudden the asserts become hard, so your test ends up simply making sure you don't get errors from the other process, which probably isn't the same one that will be used in anger so is really a mute test - after all I think it's safe to assume Javamail works ;-) The mocking approach allows you to validate the content of the message which I think is the real test....
Nick Holt