Как написать тесты для ASP.NET MVC 3 AsyncControllers с MSpec - PullRequest
4 голосов
/ 12 марта 2011

Я хочу написать TaskController для приложения ASP.NET MVC 3 для некоторых долгосрочных задач, таких как рассылка новостей пользователям сайта.Я подумал, что было бы целесообразно использовать AsyncController, поскольку отправка электронных писем может занять некоторое время, и я хочу иметь возможность сохранить некоторое состояние в базе данных, когда задача завершится.

Будучи должным образом воспитанным разработчиком,Я (: þ), и, будучи действительно в BDD, я естественно хочу начать со спецификации, используя MSpec.

Представьте, что мой контроллер выглядит так:

public class TaskController : AsyncController
{    
    readonly ISession _session;

    public TaskController(ISession session)
    {
        _session = session;
    }

    public void SendMailAsync()
    {
        // Get emails from db and send them
    }

    public ActionResult SendMailCompleted()
    {
        // Do some stuff here
    }
}

Как это сделать?идти о написании спецификаций для AsyncControllers?Представьте, что я начинаю со следующей спецификации:

public class TaskControllerContext
{
    protected static Mock<ISession> session;
    protected static TaskController controller;
    protected static ActionResult result;
}

[Subject(typeof(TaskController), "sending email")]
public class When_there_is_mail_to_be_sent : TaskControllerContext
{
    Establish context = () =>
    {
        session = new Mock<ISession>();
        controller = new TaskController(session.Object);
    };

    // is this the method to call?
    Because of = () => controller.SendMailAsync();

    // I know, I know, needs renaming...
    It should_send_mail;
}

Должен ли я вызывать метод SendMailAsync для теста?Я действительно чувствую себя отвратительно.Как мне поступить с результатом SendMailCompleted?

1 Ответ

3 голосов
/ 03 апреля 2011

Ну, я наконец-то понял, по крайней мере, как это сделать.Я уверен, что есть другие и, вероятно, лучшие способы, но здесь идет:

Объявите AutoResetEvent, который будет использоваться в качестве дескриптора ожидания, и EventHandler, который будет настроен на срабатывание при асинхронном действиизавершается.Затем в блоке Because (что соответствует части Act модульного тестирования) вызовите WaitOne для AutoResetEvent:

static AutoResetEvent waitHandle; 
static EventHandler eventHandler;
static int msTimeout = 5000;
static IEnumerable<Email> response;

Establish context = () =>
{
    toSend = new List<Email>
    {
        // Emails here
    };

    // Prepare the mock objects
    session.Setup(x => x.All<Email>()).Returns(toSend.AsQueryable());
    mailer.Setup(x => x.SendMail(Moq.It.IsAny<IEnumerable<Email>>()))
        .Returns(toSend);

    // Set up the wait handle    
    waitHandle = new AutoResetEvent(false);
    eventHandler = (sender, e) => waitHandle.Set();
    controller.AsyncManager.Finished += eventHandler;
};

Because of = () =>
{
    controller.SendMailAsync();
    if (!waitHandle.WaitOne(msTimeout, false)) {}

    response = (IEnumerable<Email>) controller.AsyncManager
        .Parameters["sentMails"];
};

It should_send_mail = () => response.Any().ShouldBeTrue();

Это также будет работать, если вы пишете модульные тестыс NUnit или любым другим тестовым фреймворком вместо MSpec.

...