Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I unit test sending email from a controller?

I'm getting started with unit testing and trying to do some TDD. I've read a fair bit about the subject and written a few tests. I just want to know if the following is the right approach.

I want to add the usual "contact us" facility on my web site. You know the thing, the user fills out a form with their email address, enters a brief message and hits a button to post the form back.

The model binders do their stuff and my action method accepts the posted data as a model. The action method would then parse the model and use smtp to send an email to the web site administrator infoming him/her that somebody filled out the contact form on their site.

Now for the question .... In order to test this, would I be right in creating an interface IMessageService that has a method Send(emailAddress, message) to accept the email address and message body. Implement the inteface in a concrete class and let that class deal with smtp stuff and actually send the mail.

If I add the interface as a parameter to my controller constructor I can then use DI and IoC to inject the concrete class into the controller. But when unit testing I can create a fake or mock version of my IMessageService and do assertions on that.

The reason I ask is that I've seen other examples of people generating interfaces for SmtpClient and then mocking that. Is there really any need to go that far or am I not understanding this stuff?

like image 933
Simon Lomax Avatar asked May 26 '10 15:05

Simon Lomax


People also ask

Should you unit test a controller?

If you've writing custom filters, routes, etc, you should unit test them, but not as part of your tests on a particular controller action. They should be tested in isolation.

How do you carry out unit testing?

A typical unit test contains 3 phases: First, it initializes a small piece of an application it wants to test (also known as the system under test, or SUT), then it applies some stimulus to the system under test (usually by calling a method on it), and finally, it observes the resulting behavior.

How long should unit test suite take to run?

Still, it seems as though a 10 second short-term attention span is more or less hard-wired into the human brain. Thus, a unit test suite used for TDD should run in less than 10 seconds. If it's slower, you'll be less productive because you'll constantly lose focus.

How do you write a unit test for a controller?

Writing a Unit Test for REST Controller First, we need to create Abstract class file used to create web application context by using MockMvc and define the mapToJson() and mapFromJson() methods to convert the Java object into JSON string and convert the JSON string into Java object.


2 Answers

You'd still need to test your class that invokes the mailer. I suggest that you might want to do a little of both. I typically create an IMailClient interface and a wrapper around SmtpClient that implements the interface. Use (and inject) the interface in a proxy class that knows how to construct the message and send it (it might have several factory-type methods that can construct multiple different types of messages). The shim around the SmtpClient really should be just that, thus there's little need for unit testing it. You can mock your shim when testing the proxy class and mock your proxy class when testing your controllers.

like image 149
tvanfosson Avatar answered Nov 15 '22 09:11

tvanfosson


The aproach you describe has worked well for me in the past. I worked on a project that interfaced with the MS Dynamics CRM web service API. I wanted to unit test my code but i had no desire to unit test the web service. I followed the exact process you describe but with the web service mocked rather than the mail delivery. It worked very well.

like image 24
Ben Robinson Avatar answered Nov 15 '22 11:11

Ben Robinson