How to write tests for ASP.NET MVC 3 AsyncControllers with MSpec

I want to write a TaskController for an ASP.NET MVC 3 application to perform some lengthy tasks, such as sending out a newsletter to site users. I thought using AsyncController would be appropriate since sending emails might take some time, and I want to keep some state in the database when the task completes.

Being a properly trained developer who I (: รพ), and being really in BDD, I naturally want to start with the specification using MSpec.

Imagine my controller looks like this:

 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 } } 

How can I write specifications for AsyncControllers? Imagine starting with the following specification:

 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; } 

Should I call the SendMailAsync method for the test? I actually feel yucky. How can I get the result from SendMailCompleted ?

+4
source share
1 answer

Well, I finally figured out at least a way to do this. I'm sure there are other and probably better ways, but here goes:

Declare an AutoResetEvent that will be used as a wait handle and an EventHandler that will be configured to run when the async action completes. Then, in the Because block (corresponding to the Act unit of testing), call WaitOne on 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(); 

This should work if you write out unit tests using NUnit or any other test environment instead of MSpec.

+3
source

Source: https://habr.com/ru/post/1343399/


All Articles