How can I verify that a method was called on a mock when the method itself is called in a delegate passed to Task.Run
? By time mock.Verify
is called the Task still hasn't executed.
I have tried await Task.Delay
just before mock.Verify
but this seems to leave the test runner hanging.
The reason for using Task.Run
is to offload the logic to prevent an attacker from being able to differentiate whether the email address exists in the system by the time to execute.
using System.Threading.Tasks;
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace AsyncTesting
{
class MyController : Controller
{
public IEmailService EmailService { get; set; }
public MyController(IEmailService emailService)
{
EmailService = emailService;
}
public ViewResult BeginPasswordReset(string emailAddress)
{
BeginPasswordResetAsync(emailAddress);
return View();
}
private Task BeginPasswordResetAsync(string emailAddress)
{
return Task.Run(delegate
{
EmailService.Send(emailAddress);
});
}
}
internal interface IEmailService
{
void Send(string emailAddress);
}
internal class MyControllerTests
{
[TestMethod]
public void BeginPasswordReset_SendsEmail()
{
var mockEmailService = new Mock<IEmailService>();
var controller = new MyController(mockEmailService.Object);
const string emailAddress = "[email protected]";
controller.BeginPasswordReset(emailAddress);
mockEmailService.Verify(es=>es.Send(emailAddress));
}
}
}
In your task you could set a ManualResetEvent (which our test code blocks on using something like:
Assert.IsTrue(yourEvent.WaitForOne(TimeSpan.FromSecond(<max time you want to wait>), "the event failed to run");
like this:
public void BeginPasswordReset_SendsEmail()
{
const string emailAddress = "[email protected]";
ManualResetEvent sendCalled= new ManualResetEvent(false);
var mockEmailService = new Mock<IEmailService>();
mockEmailService.Setup(m => m.Send(emailAddress)).Callback(() =>
{
sendCalled.Set();
});
var controller = new MyController(mockEmailService.Object);
controller.BeginPasswordReset(emailAddress);
Assert.IsTrue(sendCalled.WaitOne(TimeSpan.FromSeconds(3)), "Send was never called");
mockEmailService.Verify(es => es.Send(emailAddress));
}
Some quick research it looks like it is possible with MSTest. e.g.
[TestMethod]
public async Task BeginPasswordResetAsync();
{
await BeginPasswordResetAsync("emailAddress");
mockEmailService.Verify...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With