Error when using Machine.Specs to test class using MVC MailerBase

134 views Asked by At

I'm receiving an error when I try to test a class (FindTask) that is making a call to another class (NotificationMailer) that inherits from MVC MailerBase.

System.ArgumentNullException: Value cannot be null. Parameter name: httpContext

class FindTask
{
     public void Find()
     {
         notificationMailer.Notify("Some message").Send();
     }
}

I'm trying to verify that FindTask calls NotificationMailer::Notify.

public class NotificationMailer : MailerBase, INotificationMailer
{
    public NotificationMailer()
    {
        MasterName = "_Layout";
    }

    public virtual MvcMailMessage Notify(string message)
    {
        return Populate(x =>
        {
            x.Subject = "Some Notification";
            x.ViewName = "Notify";
            x.To.Add("[email protected]");
            x.Body = message;
        });
    }
}

The error is on the return Populate(...) line.

public class FindTaskSpec : WithSubject<FindTask>
{
    Establish context = () =>
    {
        MvcMailMessage mailMessage = new MvcMailMessage();
        The<INotificationMailer>()
            .WhenToldTo(x => x.Notify(Param<string>.IsAnything))
            .Return(mailMessage); 
    };

    Because of = () => Subject.Find();
}

public class when_something_is_found : FindTaskSpec
{
    It should_send_an_email_with_the_found_stuff = () => The<IDeadLinkMailer>()
        .WasToldTo(x => x.Notify(Param<string>.IsAnything))
        .OnlyOnce();
}

I think the line in Establish context(the mock) should dictate that if Notify is called, it should return a new MVCMailMessage without running through the body of the function.

My Questions:

  1. How can I resolve this error and test to make sure the Notify method is called?

  2. Why is it that the mock for Notify does not stop the test from entering the function body of notify?

On a side note I've already tried setting MailerBase.IsTestModeEnabled = true. This results in an Url error - Invalid Url: The Url is Empty;

I've read through their wiki page on setting up tests for MvcMailer.

I've also read almost all the other stack overflow pages on this. I think that this link was most helpful. The problem is that I don't know how to imitate what they do in this link with Machine.Specifications.

I think my issue would be resolved if I could create a mock for either of the methods shown in the links.

1

There are 1 answers

0
Tim Long On BEST ANSWER

I think in this situation I would be tempted to just create a FakeNotificationMailer class, by deriving from NotificationMailer and overriding the Notify method and having it return a pre-built MvcMailMessage. That's essentially the same as what your mock is doing, but everything is explicit. Something like:

public class FakeNotificationMailer : MailerBase, INotificationMailer
    {
    MvcMailMessage preBuiltMessage;
    public NotificationMailer(MvcMailMessage result)
        {
        preBuiltMessage = result;
        }

    public virtual MvcMailMessage Notify(string message)
        {
        return preBuiltMessage;
        }
    }

(I had problems figuring out what your dependencies are, so that's a bit of a shot in the dark).

Of course then you have to find somewhere that you can inject your fake implementation (Dependency Inversion Principle).