Moq 为什么模拟方法返回 null 并测试直到通过? 设置阶段验证阶段

如何解决Moq 为什么模拟方法返回 null 并测试直到通过? 设置阶段验证阶段

我正在尝试模拟下面的两个接口。

Mock<IEmailSender> emailSender = new Mock<IEmailSender>();
Mock<IEmailTemplate> emailTemplate = new Mock<IEmailTemplate>();

这里是设置

emailTemplate.Setup(x => x.GetForgotPasswordTemplate(It.IsAny<EmailTemplateviewmodel>())).Returns(It.IsAny<string>());
emailSender.Setup(x => x.SendEmailAsync(It.IsAny<SendEmailviewmodel>(),default)).ReturnsAsync(It.IsAny<SendEmailResultviewmodel>());

这是调用的控制器操作。

[EnableCors(PolicyName = "AllowClientAccess")]
[HttpGet("Forgot")]
public async Task<IActionResult> ForgotPassword([FromQuery] string email)
{
    var user = await _userManager.FindByEmailAsync(email);
    if (user != null)
    {
        //MOQ file path not found
        EmailTemplateviewmodel model = new EmailTemplateviewmodel();
        model.Email = email;
        model.RecipientName = user.UserName;

        var message = _emailTemplate.GetForgotPasswordTemplate(model);

        SendEmailviewmodel sendEmailviewmodel = new SendEmailviewmodel();

        sendEmailviewmodel.RecipientName = user.UserName;
        sendEmailviewmodel.RecipientEmail = user.Email;
        sendEmailviewmodel.Subject = "ForgotPassword";
        sendEmailviewmodel.Body = message;

        await _emailSender.SendEmailAsync(sendEmailviewmodel);
        return Ok(AddSuccess("Check your email","Forgot Password"));
    }

    ModelState.AddModelError("Forgot Password","Unable to send email");
    return BadRequest(ModelErrors());
}

这一行返回空

var message = _emailTemplate.GetForgotPasswordTemplate(model);

这是方法代码

public string GetForgotPasswordTemplate(EmailTemplateviewmodel model)
{
    try
    {
        var utcNow = DateTime.Now;
        if (_testemailTemplate == null)
            if (File.Exists("Helpers/Templates/ForgotPasswordEmail.template"))
                _testemailTemplate = ReadPhysicalFile("Helpers/Templates/ForgotPasswordEmail.template");

        var appUrl = _configuration.GetSection("ApplicationUrl").Value +
                     "/reset-password?&email=" + model.Email;
        var emailMessage = _testemailTemplate
            .Replace("{user}",model.RecipientName)
            .Replace("{testDate}",utcNow.ToString(CultureInfo.InvariantCulture))
            .Replace("{appUrl}",appUrl);

        return emailMessage;
    }
    catch (Exception e)
    {
        Log.Warning(e,"Email error");
        throw;
    }
}

这一行也返回空

await _emailSender.SendEmailAsync(sendEmailviewmodel);

这是方法代码

public Task<SendEmailResultviewmodel> SendEmailAsync(SendEmailviewmodel model,SmtpConfig config = default)
{
    model.IsHtml = true;
    
    var from = new MailBoxAddress(_config.FromName,_config.FromEmail);
    var to = new MailBoxAddress(model.RecipientName,model.RecipientEmail);

    return SendEmailAsync(@from,new[] {to},model.Body,config,model.IsHtml);
}

这里是测试

[Theory]
[InlineData("stephen@kaizenappz.com")]
public async Task WhenAUserForgetsPasswordAHttpStatusCode200ShouldBeReturnedAsync(string email)
{
    var confirmUser = await Controller.ForgotPassword(email);

    var result = confirmUser as OkObjectResult;

    var actual = (HttpStatusCode)result?.StatusCode.Value;
    var expected = HttpStatusCode.OK;

    Assert.AreEqual(expected,actual);
}

但是测试通过了,我想知道为什么这两种方法都返回 null 以及为什么即使它返回 null 测试也通过了。我如何让这些东西返回?

我不明白的一件事是什么时候使用 It.Any 并且只传递带有一些测试数据的普通对象。如果我使用 It.Any 并且我需要将模型传递到我的控制器操作中,我应该如何检查用户是否存在?

解决方法

设置阶段

每当您需要模拟接口方法时,请尝试在设置过程中放任自流。
换句话说,允许接收任何参数:

const string mockedForgotPwdTemplate = "...";
emailTemplate
  .Setup(template => template.GetForgotPasswordTemplate(It.IsAny<EmailTemplateViewModel>()))
  .Returns(mockedForgotPwdTemplate);

如果你的返回值取决于参数
然后使用 Returns 的重载,它接受一个函数:

const string mockedTemplateWithSubject = "..."; 
const string mockedTemplateWithoutSubject = "...";

emailTemplate
  .Setup(template => template.GetForgotPasswordTemplate(It.IsAny<EmailTemplateViewModel>()))
  .Returns((EmailTemplateViewModel vm) => !string.IsNullOrEmpty(vm.Subject) ? mockedTemplateWithSubject : mockedTemplateWithoutSubject);

验证阶段

在断言期间尽量具体。
如果您有权访问该参数,则将其传递给 Verify:

var mockedViewTemplate = new EmailTemplateViewModel { ... };

emailTemplate
  .Verify(template => template.GetForgotPasswordTemplate(mockedViewTemplate),Times.Once);

请记住,moq 使用引用检查来确定 expectedactual 参数是否相同。如果您没有对此参数的引用,则应使用 It.Is<T>:

const string expectedSubject = "ForgotPassword";

emailTemplate
  .Verify(template => template.GetForgotPasswordTemplate(
     It.Is<EmailTemplateViewModel>(vm => expectedSubject == vm.Subject),Times.Once);

或者如果您希望对多个属性进行断言,则:

private bool AssertViewModel(EmailTemplateViewModel actualVM,string expectedSubject,string expectedRecipientName)
{
   Assert.Equal(expectedSubject,actualVM.Subject);
   Assert.Equal(expectedRecipientName,actualVM.RecipientName);

   return true;
}  

//...

const string expectedSubject = "ForgotPassword",expectedRecipent = "...";

emailTemplate
  .Verify(template => template.GetForgotPasswordTemplate(
     It.Is<EmailTemplateViewModel>(vm => this.AssertViewModel(vm,expectedSubject,expectedRecipient)),Times.Once);

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?