Dependency Injection and mocking is your friend. Whenever you find something hard to test, it usually means your class is having too many responsibilities. I'm assuming you are sending your emails using some library. That library most likely takes your message info (destination address, subject, body). What you want to do, is decouple your business logic that's in charge of collecting all email addresses, email subject and body, from that 3rd party library. How? By abstracting that library using an interface, and then injecting it into your Email class.
IEmailLibrary
{
void SendEmail(EmailInfo emailInfo);
}
class Email
{
private IEmailLibrary _emailLibrary;
public Email(IEmailLibrary emailLibrary)
{
_emailLibrary = emailLibrary;
}
public void SendEmailNotifications()
{
EmailInfo emailInfo = ComposeEmail();
_emailLibrary.SendEmail(emailInfo);
}
private EmailInfo ComposeEmail()
{
//your business logic
}
}
Now in your production code, you would create a real EmailLibrary like this:
public RealEmailLibrary : IEmailLibrary
{
...
public SendEmail(EmailInfo emailInfo)
{
_3rdPartyEmailComponent.SendEmail(emailInfo....);
}
}
and then inject it into your Email class, like this:
RealEmailLibrary realEmailLibrary = new RealEmailLibrary();
Email email = new email(realEmailLibrary);
And if you wanted to test your business logic, all you need to do in your test code, is inject either a mock, or your own stub that implements an IEmailLibrary interface. For example:
public FakeEmailLibrary : IEmailLibrary
{
public EmailInfo _iWasCalledWithThisEmailInfo;
public void SendEmail(EmailInfo emailInfo)
{
_iWasCalledWithThisEmailInfo = emailInfo;
}
}
You inject it in your test like this:
FakeEmailLibrary fakeEmailLibrary = new FakeEmailLibrary();
Email email = new email(fakeEmailLibrary);
email.SendEmailNotifications();
AssertStuff(fakeEmailLibrary.iWasCalledWithThisEmailInfo, expectedEmailInfo);
I recommend using mocking frameworks, as opposed to my stub example (like Moq for C#), which make it really easy to verify your injected dependencies were called with the right arguments.
void
methods either modify their parameters or perform side-effects (or both), and parameterlessvoid
methods can only perform side-effects!void
methods are always about the side effects