alex kostin alex kostin - 1 month ago 12
C# Question

Unit-testing mail sending with postal and moq

System.ArgumentNullException
is thrown from
System.Web.HttpBrowserCapabilities
constructor when
mail.Send()
from test called. It requires
httpBrowserCapabilities
parameter.

Code I have used:

EmailModel:

public class EmailModel :Email
{
public EmailModel() :base("EmailModel")
{

}
public string To = "**********@gmail.com";

[Display(ResourceType = typeof(Resource), Name = "Name")]
[Required(ErrorMessageResourceName = "Error_NameRequired", ErrorMessageResourceType = typeof(Resource))]
public string Name { get; set; }

[DataType(DataType.EmailAddress)]
[Display(ResourceType = typeof (Resource), Name = "Email")]
[Required(ErrorMessageResourceName = "Error_EmailRequired", ErrorMessageResourceType = typeof(Resource))]
[RegularExpression(".+@.+", ErrorMessageResourceName = "Error_EmailWrong", ErrorMessageResourceType = typeof(Resource))]
public string Email { get; set; }

[Display(ResourceType = typeof(Resource), Name = "Topic")]
[Required(ErrorMessageResourceName = "Error_TopicRequired", ErrorMessageResourceType = typeof(Resource))]
public string Topic { get; set; }

[Display(ResourceType = typeof(Resource), Name = "Massage")]
[DataType(DataType.MultilineText)]
[Required(ErrorMessageResourceName = "Error_MassageRequired", ErrorMessageResourceType = typeof(Resource))]
public string Massage { get; set; }
}


View of message:

@model *******.Models.EmailModel
@{
Layout = null;
}
To: @Model.To
Subject: @Model.Topic

The massage from @Model.Name @Model.Email
Massage text:
@Model.Massage


UnitTest :

public class EmailSetter
{
public const string DefaultName = "NameTest";
public const string DefaultEmail = "EmailTest@domaintest.test";
public const string DefaultTopic = "TopicTest";
public const string DefaultMassage = "Massage test.";
public EmailModel GetEmail(string Name = DefaultName, string Topic = DefaultTopic, string Email = DefaultEmail, string Massage = DefaultMassage)
{
EmailModel email = new EmailModel();
email.Name = Name;
email.Topic = Topic;
email.Email = Email;
email.Massage = Massage;
return email;
}
}
[TestClass]
public class MailTests
{
[TestMethod]
public void MailSendWithRightModel()
{
//Arrange
HomeController controller = new HomeController();
Mock<EmailModel> email = new Mock<EmailModel>();
email.Object.Name = EmailSetter.DefaultName;
email.Object.Email = EmailSetter.DefaultName;
email.Object.Topic = EmailSetter.DefaultTopic;
email.Object.Massage = EmailSetter.DefaultMassage;
//Act
controller.Contact(email.Object);
//Assert
email.Verify(mail => mail.Send());
}
}


Controller from test:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Contact(EmailModel emailModel)
{
if(ModelState.IsValid)
{
ViewBag.Send = true;
emailModel.Send();
ModelState.Clear();
}
return View(emailModel);
}


What I have tried (in unit test method):


  1. creating
    new HttpBrowserCapabilities
    through
    new
    BrowserCapabilitiesFactory

  2. creating
    new
    HttpBrowserCapabilitiesWrapper
    with
    HttpBrowserCapabilities

    object



Any ideas how to make code not to throw exception? (i.e. make current HttpBrowserCapabilitiesWrapper get existing HttpBrowserCapabilities) :)

Answer

Source: Postal Unit Testing

When unit testing code that uses Postal, you may want to verify that an email would be sent, without actual sending it.

Postal provides an interface IEmailService and an implementation, EmailService, which actually sends email.

Assuming you use some kind of IoC container, configure it to inject an IEmailService into your controller.

Then use the service to send email objects (instead of calling Email.Send()).

public class HomeController : Controller {

    readonly IEmailService emailService;

    public HomeController(IEmailService emailService) {
        this.emailService = emailService;
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Contact(EmailModel email) {
        if(ModelState.IsValid) {
            emailService.Send(email);
            ViewBag.Send = true;
            ModelState.Clear();
        }
        return View(email);
    }
}

Test this controller by creating a mock of the IEmailService interface.

[TestClass]
public class MailTests {
    [TestMethod]
    public void MailSendWithRightModel() {
        //Arrange
        var mockService = new Mock<IEmailService>();
        var controller = new HomeController(mockService.Object);
        var email = new EmailSetter().GetEmail();

        //Act
        controller.Contact(email);

        //Assert
        mockService.Verify(m => m.Send(email));
    }
}