Using StructureMap Inject To Avoid External Dependencies In Integration Tests

Posted almost 2 years ago on July 16, 2011

At the end of last week I wrote a class to manage sending e-mail from our application. This class (called it EmailProcessingManager) had all the usual suspects:

  • Get a list of to, cc, bcc, from and reply to email addresses
  • Find a template
  • Replace tokens in said template with values from the database
  • Send the email(s)

Wound up making a class for each one of these separate duties and wrote tests for each. But to seal it all together, I wanted to write an integration test that would test the whole operation of sending an email. This test would hit the database, pull in the actual HTML template, replace token values, etc. But what about the actual sending of email? I didn't want to go to the trouble of trying to figure out how to hit a pop email inbox and reading in the email...there had to be an easier way!

ObjectFactory.Inject() to the rescue!

The problem I had was that I wanted to use the entire EmailProcessingManager class as it would be used in production, except I didn't want the mail to actually go out. I wanted all the classes' dependencies to be real implementations except for the class that actually spun up the SMTP client class to send the mail. That's when I found the Inject method.

(Using Moq in this example)

<TestFixtureSetUp()>
Public Sub Setup()
    _mailSenderMock = New Mock(Of IEmailSender)
    _mailSenderMock.Setup(Function(mock) mock.SendEmail(It.IsAny(Of List(Of MailMessage)))) _
        .Callback(Sub(messages) _emailsThatWouldHaveBeenSent = messages)
        
    'Inject the mocked object instead of using the production code
    StructureMap.ObjectFactory.Inject(Of IEmailSender)(_mailSenderMock.Object)

    'Get an instance of the class under test
    _emailProcessingManager = StructureMap.ObjectFactory.GetInstance(Of IEmailProcessingManager)()        
    
    Dim recipientList = New List(Of String) From {"paco@joescrabshack.com"}
    _emailProcessingManager.ProcessEmailRequest(EmailType.Registration, recipientList)   
End Sub

<Test()>
Public Sub Can_create_a_request_for_an_authorization_email_and_have_it_sent_to_the_recipient()
    _emailsThatWouldHaveBeenSent.First(Function(email) email.To.First().Address = "paco@joescrabshack.com").ShouldNotBeNull()
End Sub  

All of the value of an integration test, but none of the pain of dealing with an external dependency. I like it.

Now that I've discovered this, I have other ideas for it's use. One situation that comes to mind is an integration test that saves a complex object graph to the database. Those tests are always hard to clean up after, so instead of saving the object I'll just use a mock to retrieve the passed object and do my asserts. No messy cleanup!

Comments

New Comment