Reputation: 17196
I have a method like
public abstract class Base
{
public void MethodUnderTest();
}
public class ClassUnderTest : Base
{
public override MethodUnderTest()
{
if(condition)
{
IMail mail = new Mail() { /* ... */ };
IMailer mailer = new Mailer() { /* ... */ }
mailer.Send(mail);
}
else
{
/* ... */
}
}
}
I have unit tests for this method, and the mail gets sent to myself, so it's not terrible (better than no test) but I'd prefer not to send the mail.
What else can I do?
(note: IMail and IMailer are part of an external library for sending e-mail. It's written in house, so I can modify it all I like if necessary, though I can't see a need to in this situation)
Upvotes: 4
Views: 897
Reputation: 57902
You can intercept the call to send the mail at many levels, but you still have to intercept it somewhere, or put up with receiving the emails.
This could be:
Put up with the test emails.
Add an email filter that deletes the emails or stores them in a different folder so they don't bother you.
Send your test emails to a different email address. Set up your server to just delete these emails when received (or keep them for 14 days like spam, so you can review them if you wish).
Replace your email server with a fake local one
Replace the IMailer's imailer.dll or IMailer implementation class with an entire mocked equivalent, or with one that siply doesn't implement the full SendMail behaviour.
Add a base class method to SendMail(), and disable its behaviour in the base class when running a unit test.
Add a virtual SendMail() method in ClassUnderTest and then create a derived class to be unit tested that simply overrides SendMail() with a mocked implementation.
Pass in the interface/object that it uses to call SendMail
Pass in a flag to indicate if it is being tested.
Redesign the class entirely so that it has a SendReport() and the report might be sent by email, TCP/IP, log file, etc.
etc.
The best approaches don't require changes to the actual method being tested, of course. Pick whichever one works best for your situation, and the other unit tests you need to add for this class.
Upvotes: 0
Reputation: 8744
I would do something like this (considering you don't have a DI framework):
public class ClassUnderTest : Base
{
private IMail mail;
private IMailer mailer
public ClassUnderTest()
{
mail = new Mail() { /* ... */ };
mailer = new Mailer() { /* ... */ }
}
public ClassUnderTest(IMail mail, IMailer mailer)
{
this.mail = mail;
this.mailer = mailer;
}
public override MethodUnderTest()
{
if(condition)
{
mailer.Send(mail);
}
else
{
/* ... */
}
}
}
Then in your test, just call the second constructor, rather than the default one.
Upvotes: 1
Reputation: 48593
A standard approach using dependency injection would be to require an IMailer
in ClassUnderTests
's constructor. If you do that, you pass a mock mailer into your tests, and the base class doesn't need to know anything about mailing or mailers.
If that's undesirable for some reason (this is pretty rare, it's usually only relevant when you don't control the underlying classes), you can use setter injection ("property injection").
Upvotes: 7
Reputation: 17020
You could setup a really simple fake SMTP server that knows just enough to verify the client sends the email.
Upvotes: 2
Reputation: 6353
You (may) can use a pickup directory and set it to a directory that is not configured to send:
http://www.singular.co.nz/blog/archive/2007/11.aspx
http://forum.discountasp.net/showthread.php?t=4593
Upvotes: 2
Reputation: 9680
My answer in general (I don't know C#)
I once needed to make something like this in Java. I made the test to check if the port of sending opens then that means it's almost done. I force stopping of the process.
Upvotes: 0