【问题标题】:Verifying Mock method was called inside Task.Run在 Task.Run 中调用了验证 Mock 方法
【发布时间】:2015-10-22 02:17:27
【问题描述】:

当方法本身在传递给Task.Run 的委托中调用时,如何验证在模拟上调用了方法?到 mock.Verify 被调用时,Task 仍未执行。

我在mock.Verify 之前尝试过await Task.Delay,但这似乎让测试运行程序挂起。

使用Task.Run的原因是为了卸载逻辑,防止攻击者在执行的时候能够区分系统中是否存在邮件地址。

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@domain.com";

            controller.BeginPasswordReset(emailAddress);

            mockEmailService.Verify(es=>es.Send(emailAddress));
        }
    }
}

【问题讨论】:

  • 我不确定 MSTest 但是使用 NUnit 你可以有一个异步测试方法并等待BeginPasswordResetAsync 方法。该任务将在等待后完成。查看使用 MSTest 进行的异步测试。
  • The reason for using Task.Run... 抱歉,但这是在您的应用程序中引入这样的故障点的糟糕理由。 Task.Run 在 ASP.NET 上非常危险;至少,使用HostingEnvironment.QueueBackgroundWorkItem
  • 谢谢@StephenCleary,我会仔细阅读您对blog.stephencleary.com/2013/10/…的主题的讨论
  • @RDay:我在fire and forget 上的帖子更直接适用于这里。

标签: c# unit-testing moq mstest


【解决方案1】:

在您的任务中,您可以设置 ManualResetEvent(我们的测试代码会阻止使用类似的东西:

Assert.IsTrue(yourEvent.WaitForOne(TimeSpan.FromSecond(<max time you want to wait>), "the event failed to run");

像这样:

public void BeginPasswordReset_SendsEmail()
{
    const string emailAddress = "email@domain.com";

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

【讨论】:

  • 太完美了,正是我想要的。
【解决方案2】:

一些快速研究似乎可以使用 MSTest。例如

[TestMethod]
public async Task BeginPasswordResetAsync();
{
    await BeginPasswordResetAsync("emailAddress");

    mockEmailService.Verify...
}

【讨论】:

  • 我试过这个,它似乎挂起了测试运行器。实际的异步方法也是控制器私有的。
猜你喜欢
  • 1970-01-01
  • 2017-03-01
  • 2020-11-08
  • 2019-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-24
  • 1970-01-01
相关资源
最近更新 更多