views:

552

answers:

4

Hi

So I have some SMTP stuff in my code and I am trying to unit test that method.

So I been trying to Mockup MailMessage but it never seems to work. I think none of the methods are virtual or abstract so I can't use moq to mock it up :(.

So I guess I have to do it by hand and that's where I am stuck.

*by hand I mean witting the interface and the wrapper but letting moq still mockup the interface.

I don't know how to write my Interface and my Wrapper(a class that will implement the interface that will have the actual MailMessage code so when my real code runs it actually does the stuff it needs to do).

So first I am not sure how to setup my Interface. Lets take a look at one of the fields that I have to mockup.

MailMessage mail = new MailMessage();

mail.To.Add("[email protected]");

so this is the first thing that I have to fake.

so looking at it I know that "To" is a property by hitting F12 over "To" it takes me to this line:

public MailAddressCollection To { get; }

So it is MailAddressCollection Property. But some how I am allowed to go further and do "Add".

So now my question is in my interface what do I make?

do I make a property? Should this Property be MailAddressCollection?

Or should I have a method like?

void MailAddressCollection To(string email);

or 

void string To.Add(string email);

Then how would my wrapper look?

So as you can see I am very confused. Since there is so many of them. I am guessing I just mockup the ones I am using.

edit code

I guess in in a true sense I would only have to test more the exceptions but I want to test to make sure if everything gets sent then it will get to response = success.

string response = null;
            try
            {

                MembershipUser userName = Membership.GetUser(user);

                string newPassword = userName.ResetPassword(securityAnswer);

                MailMessage mail = new MailMessage();

                mail.To.Add(userName.Email);

                mail.From = new MailAddress(ConfigurationManager.AppSettings["FROMEMAIL"]);
                mail.Subject = "Password Reset";

                string body = userName + " Your Password has been reset. Your new temporary password is: " + newPassword;

                mail.Body = body;
                mail.IsBodyHtml = false;


                SmtpClient smtp = new SmtpClient();

                smtp.Host = ConfigurationManager.AppSettings["SMTP"];
                smtp.Credentials = new System.Net.NetworkCredential(ConfigurationManager.AppSettings["FROMEMAIL"], ConfigurationManager.AppSettings["FROMPWD"]);

                smtp.EnableSsl = true;

                smtp.Port = Convert.ToInt32(ConfigurationManager.AppSettings["FROMPORT"]);

                smtp.Send(mail);

                response = "Success";
            }
            catch (ArgumentNullException ex)
            {
                response = ex.Message;

            }
            catch (ArgumentException ex)
            {
                response = ex.Message;

            }
            catch (ConfigurationErrorsException ex)
            {
                response = ex.Message;
            }
            catch (ObjectDisposedException ex)
            {
                response = ex.Message;
            }
            catch (InvalidOperationException ex)
            {
                response = ex.Message;
            }
            catch (SmtpFailedRecipientException ex)
            {
                response = ex.Message;
            }
            catch (SmtpException ex)
            {
                response = ex.Message;
            }



            return response;

        }

Thanks

A: 

In .NET 4.0 you can use the "duck-typing" to pass another class instead of "System.Net.Mail". But in earlier version, I'm afraid there is no another way, than create wrapper around "System.Net.Mail" nad the mock class.

If is another (better) way, I would like to learn it :).

EDIT:

public interface IMailWrapper {
    /* members used from System.Net.Mail class */
}

public class MailWrapper {
    private System.Net.Mail original;
    public MailWrapper( System.Net.Mail original ) {
        this.original = original;
    }

    /* members used from System.Net.Mail class delegated to "original" */
}

public class MockMailWrapper {
   /* mocked members used from System.Net.Mail class */
}


void YourMethodUsingMail( IMailWrapper mail ) {
    /* do something */
}
TcKs
So how would I do it then? Like in my wrapper should I have // property of Mail Herepublic MyConstructor(){ Mail = new MailMessage(0;}public void To.Add(string email){ Mail.To.Add(email);}Then do it the same thing for every single method I need?Or can I do it someway else then that?
chobo2
Yes, I think there is no other way, how to test it.
TcKs
+1  A: 

You will end up mocking several different classes here (at least two). First, you need a wrapper around the MailMessage class. I would create an interface for the wrapper, then have the wrapper implement the interface. In your test, you will mock up the interface. Second, you'll provide a mock implementation as an expectation to the mocked interface for the MailAddressCollection. Since MailAddressCollection implements Collection<MailAddress>, this should be fairly straight-forward. If mocking the MailAddressCollection is problematic due to additional properties (I didn't check), you could have your wrapper return it as an IList<MailAddress>, which as an interface should be easy to mock.

public interface IMailMessageWrapper
{
    MailAddressCollection To { get; }
}

public class MailMessageWrapper
{
    private MailMessage Message { get; set; }

    public MailMessageWrapper( MailMessage message )
    {
        this.Message = message;
    }

    public MailAddressCollection To
    {
        get { return this.Message.To; }
    }
}

// RhinoMock syntax, sorry -- but I don't use Moq
public void MessageToTest()
{
     var message = MockRepository.GenerateMock<IMailMessageWrapper>()
     var to = MockRepository.GenerateMock<MailAddressCollection>();

     var expectedAddress = "[email protected]";

     message.Expect( m => m.To ).Return( to ).Repeat.Any();
     to.Expect( t => t.Add( expectedAddress ) );
     ...
}
tvanfosson
Thanks I am not sure if I need to actually mock it up now. This is very useful since when I do my mocking up of some of the stuff then I can follow this.I am not sure about something though. What happens if I want to use the To.Add();Do I have to make property with that one?On a side notes do you know how they made it so they can doMail.To.Add(...) like how did they get the "add" method to show up after "To" I even seen they did a property after a property.
chobo2
+14  A: 

Why mock the MailMessage? The SmtpClient receives MailMessages and sends them out; that's the class I'd want to wrap for testing purposes. So, if you're writing some type of system that places Orders, if you're trying to test that your OrderService always emails when an order is placed, you'd have a class similar to the following:

class OrderService : IOrderSerivce 
{
    private IEmailService _mailer;
    public OrderService(IEmailService mailSvc) 
    {
        this. _mailer = mailSvc;
    }

    public void SubmitOrder(Order order) 
    {
        // other order-related code here

        System.Net.Mail.MailMessage confirmationEmail = ... // create the confirmation email
        _mailer.SendEmail(confirmationEmail);
    } 

}

With the default implementation of IEmailService wrapping SmtpClient:

This way, when you go to write your unit test, you test the behavior of the code that uses the SmtpClient / EmailMessage classes, not the behavior of the SmtpClient / EmailMessage classes themselves:

public Class When_an_order_is_placed
{
    [Setup]
    public void TestSetup() {
        Order o = CreateTestOrder();
        mockedEmailService = CreateTestEmailService(); // this is what you want to mock
        IOrderService orderService = CreateTestOrderService(mockedEmailService);
        orderService.SubmitOrder(o);
    } 

    [Test]
    public void A_confirmation_email_should_be_sent() {
        Assert.IsTrue(mockedEmailService.SentMailMessage != null);
    }


    [Test]
    public void The_email_should_go_to_the_customer() {
        Assert.IsTrue(mockedEmailService.SentMailMessage.To.Contains("[email protected]"));
    }

}

Edit: to address your comments below, you'd want two separate implementations of EmailService -- only one would use SmtpClient, which you'd use in your application code:

class EmailService : IEmailService {
    private SmtpClient client;

    public EmailService() {
        client = new SmtpClient();
        object settings = ConfigurationManager.AppSettings["SMTP"];
        // assign settings to SmtpClient, and set any other behavior you 
        // from SmtpClient in your application, such as ssl, host, credentials, 
        // delivery method, etc
    }

    public void SendEmail(MailMessage message) {
        client.Send(message);
    }

}

Your mocked / faked email service (you don't need a mocking framework for this, but it helps) wouldn't touch SmtpClient or SmtpSettings; it'd only record the fact that, at some point, an email was passed to it via SendEmail. You can then use this to test whether or not SendEmail was called, and with which parameters:

class MockEmailService : IEmailService {
    private EmailMessage sentMessage;;

    public SentMailMessage { get { return sentMessage; } }

    public void SendEmail(MailMessage message) {
        sentMessage = message;
    }

}

The actual testing of whether or not the email was sent to the SMTP Server and delivered should fall outside the bounds of your unit testing. You need to know whether this works, and you can set up a second set of tests to specifically test this (typically called Integration Tests), but these are distinct tests separate from the code that tests the core behavior of your application.

Jeremy Frey
Hmm I think I understand what your saying but lets double chekc.Are you saying that because MailMessage is all code that was written for me I don't have to test and since MailMessage has really nothing to do with my tests also that Mailmessage does not really cause any dependencies right it is really just smpt.send() that creates the dependency right?So really I just have to fake the send part to break any dependencies right?My thinking was but MailMessage was a dependency but now that I look at it I can see it really is not.Is this correct and what you where trying to say?
chobo2
OH and one more thing I use stuff likeConfigurationManager.AppSettings["SMTP"];So should I mockup ConfigurationManager to break dependency on a AppConfig file or should I just make an appConfig file?
chobo2
Right. You don't want to test MailMessage or SmtpClient; it's not your code. You want to unit test everything that USES MailMessage and SmtpClient to make sure that: a) they are creating a MailMessage with the correct fields; and b) are actually sending the email.
Jeremy Frey
For ConfigurationManater.AppSettings["SMTP"], this would be a property of your "real" implementation IEmailService, the one that uses SmtpClient. But it's not a property of IEmailService itself, and it's not a property of any code that uses SmtpClient, so it doesn't need to be tested. To put it another way, when you execute your unit test, ConfigurationManager.AppSettings["SMTP"] won't even be touched.
Jeremy Frey
Hi I am not sure what you mean the configmang stuff won't be touched.I am also wondering I will have to mockup all the stuff I use then from the smtp client right?Like I use Host,enableSLL and extra. I know I am not testing that but I can't just have in my "IEmailService" just send since I got to setup those other things and the only way to do that is what make a smtpClient object and pass it in?
chobo2
I guess unless in the send method this stuff is set at that time?
chobo2
oh one more thing say they where not setup in the send and in my wrapper how would I make them? like would I have a method for every one they have?like say for smtp.CredentialsWould I make a method that will have this line in it or can I have I property like they have it?The thing I am wondering with properties are what happens if I add smtp.Credentials.GetCredential() now how would I expand my property to look like theirs?I guess what I am trying to do is trying to make it look as close as possible to the orginal code.
chobo2
Hey I am going to post my code since right now how your solution sounds it seems like I should be moving my smtp stuff in its own class.
chobo2
Oh and if I used a mock framework then I could just skip making your MockEmailService class right?
chobo2
Yes, if you use a mock framework you can skip making the MockEmailService. You'd want to set that Mock up to verify that both the SendEmail method was called, and that the email contains the fields you expect. Look at the Callbacks section of the Moq quickstart (http://code.google.com/p/moq/wiki/QuickStart), specifically the second example
Jeremy Frey
A: 

disclaimer: I work at Typemock Instead of finding some hack you can use Typemock Isolator to simply fake that class in only one line of code:

var fakeMailMessage = Isolate.Fake.Instance<MailMessage>();

Then you can set behavior on it using Isolate.WhenCalled

Dror Helper
Ya I hear typemock is good too bad it costs money :( Just looking the cheapest version is like $89. Not sure what you get but at this time I can't afford to buy things like this maybe when I am down school and have a job or do contracting then it will be worth it to buy.
chobo2
@chobo2 you have to admit that using Isolator produces the most elegant and easy to userstand code that solve this problem. I guess that quality has its price.Are you programing with Notepad because Visual Studio costs money (much more then 90$)
Dror Helper