0

Simple question from theoretical point of view. If i have Email class with Send() method inside which is responsible for sending email. Send() method is declared in interface called IEmail. Let's assume i will moq that method as IEmail interface in my unit test. Then when i Setup moq with Send() and later on use Verify() method then i suppose real email will be not send but only check whether Send() method was called properly. Am i right? Second to check real Send() to really send email i should use integration test. Is that all correct?

Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
abc
  • 19
  • 3

1 Answers1

2

Presumably what you're testing isn't the actual Email class. If it's a concrete class that sends an email, then calling Send will (and probably should) send an email.

Mocking would be useful for testing a class that depends on your IEmail interface. It allows you to verify that your class does what is expected with the interface it depends on. In that case you wouldn't want to send an email. You just want to test that your class tells IEmail to send an email.

Here's a contrived example of a class that depends on an IEmail interface to send an email.

public class ClassThatSendsEmail
{
    private readonly IEmail _email;

    public ClassThatSendsEmail(IEmail email)
    {
        _email = email;
    }

    public void DoSomethingThatCausesAnEmailToGetSent()
    {
        var message = new Message {To = "bob@bob.com", Body = "Hi, Bob!"};
        _email.Send(message);
    }
}

...and here are some other types I threw together just for testing:

public interface IEmail
{
    void Send(Message message);
}

public class Message
{
    public string To { get; set; }
    public string Body { get; set; }
}

Here's a very simple test that will mock IEmail.

public class Tests
{
    [TestCase]
    public void MyClassSendsAnEmail()
    {
        var emailMock = new Mock<IEmail>();
        emailMock.Setup(x=>x.Send(It.IsAny<Message>())).Verifiable();
        var subject = new ClassThatSendsEmail(emailMock.Object);
        subject.DoSomethingThatCausesAnEmailToGetSent();
        emailMock.Verify(x=>x.Send(It.IsAny<Message>()));
    }
}

To directly answer your question, no email will get sent because the concrete Email class is nowhere in the picture. I haven't even created one. My class only depends on the interface, so I can test it before I've even written an implementation of IEmail. The test is just going to verify that my class called the Send method of the mock.

(This is a great way to prevent coupling. I don't want my class to be tightly coupled to some other class that sends emails, so that the one class only works with the other. If the first class works as expected when I haven't even created the second class then it's not coupled to the second class.)

Sometimes it's convenient to write a test double, which is an actual class that implements the interface just for test purposes. You can do this same stuff with Moq, but sometimes all the setup code gets complicated and using a test double just makes it easier to read.

So here's a test double for IEmail. When you send a message it's going to add the message to a list. That way after the test is run you can look at what's in the list to see if the correct email got sent:

public class EmailListDouble : List<Message>, IEmail
{
    public void Send(Message message)
    {
        Add(message);
    }
}

Now you can easily write a test which verifies that IEmail.Send was called and that the expected message was sent:

[TestCase]
public void MyClassSendsAnEmail()
{
    var email = new EmailListDouble();
    var subject = new ClassThatSendsEmail(email);
    subject.DoSomethingThatCausesAnEmailToGetSent();
    Assert.True(email.Any(message=> message.To == "bob@bob.com" && message.Body.Contains("Bob")));
}

Moq is great, but sometimes the setup can not only become a little cumbersome, but it also makes it harder to tell what's getting tested. Sometimes just using a test double gets the job done and it's easier.

Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
  • and how integration test could look like, could you post some example based on that? P.S In your second test the real email will be send because i do not see moq? And is it more like integration test then? – abc Mar 05 '19 at 16:38
  • I googled and found this, which I never knew about: https://stackoverflow.com/questions/3998436/how-to-test-sending-emails-from-asp-net-on-development-machine. Assuming that you're using an SMTP client, you could configure it to "send" emails to a folder, and then you could read them and inspect them. I've never integration tested actual emails, though. If I'm using the framework classes then there doesn't seem to be a need. That would seem more like testing the infrastructure. But I guess there's a case for everything. – Scott Hannen Mar 05 '19 at 16:43