【问题标题】:Using Mock.Setup on two different methods doesn't match on one and instead returns null在两种不同的方法上使用 Mock.Setup 与一种方法不匹配,而是返回 null
【发布时间】:2016-01-07 12:52:49
【问题描述】:

我是单元测试的新手。我正在使用 Moq 进行单元测试。我有一种情况,我必须在同一部分模拟两种不同的方法:

我有一个如下的操作方法:

public ActionResult Login(someparameters)
{
   //code...
  var user = userRepository.SelectAllUserByEmail(someparamters); //first method
  //....
  var userDetails = userRepository.ValidateUser(someparameters);//second method
}

这是我的单元测试部分:

userrepositoryMock.Setup(r => r.SelectAllUserByEmail(someparameters))
  .Returns(new List<User>() { new User { Salt = strSalt, FundraiserAdminId = fundadmind, StatusCode = statusCode, UserTypeId = userTypeId, HomePageURL = homepageURL, OrganizationId = organizationId } } );

userrepositoryMock.Setup(k => k.ValidateUser(someparamters))
.Returns(new User { Salt = strSalt, FundraiserAdminId = fundadmind, StatusCode = statusCode, UserTypeId = userTypeId, HomePageURL = homepageURL, OrganizationId = organizationId });

但这只会模拟SelectAllUserByEmailValidateUser 方法,它返回null。

【问题讨论】:

  • 你应该检查传递给方法参数的参数

标签: asp.net-mvc unit-testing moq


【解决方案1】:

您还没有指定someparameters 代表什么类型(或多个类型),但我敢打赌,其中至少有一个是引用类型(不是简单的字符串)。

对于引用类型(如对象实例),在精确实例上使用 Moq 的 .Setup 通常是个坏主意,因为这需要将完全相同的引用传递给模拟类,以便设置匹配和返回提供的输出。

这是一个重现问题的简单 MVCE。给定以下代码:

public class User
{
    public string Name { get; set; }
}

public interface IMyInterface
{
    string GetUserName(User user);
}

以下单元测试表明,如果将另一个引用 (sameUser) 传递给 Mock,则绑定到特定对象实例 (aUser) 的 Setup匹配: p>

[Test]
public void TestGetUserBad()
{
    var mock = new Mock<IMyInterface>();

    var aUser = new User { Name = "User1" };
    var sameUser = new User { Name = "User1" };

    mock.Setup(x => x.GetUserName(aUser)).Returns<User>(u => u.Name);

    Assert.AreEqual("User1", mock.Object.GetUserName(aUser), 
        "The mock has been setup for aUser, so this works");
    Assert.AreEqual(null, mock.Object.GetUserName(sameUser), 
        "aUser is a different reference than sameUser hence fails");
}

相反,您应该使用 Moq 的 It.Is&lt;&gt;(带有谓词)或 It.IsAny&lt;&gt;(任意)matchers 以允许匹配符合谓词(如果有)的任何引用。

[Test]
public void TestGetUserGood()
{
    var mock = new Mock<IMyInterface>();

    var aUser = new User { Name = "User1" };
    var sameUser = new User { Name = "User1" };

    mock.Setup(x => x.GetUserName(It.IsAny<User>())).Returns<User>(u => u.Name);

    Assert.AreEqual("User1", mock.Object.GetUserName(aUser), 
        "The mock has been setup for any user, so this works");
    Assert.AreEqual("User1", mock.Object.GetUserName(sameUser), 
        "The mock has been setup for any user, so this works");
}

编辑

出于兴趣,如果您怀疑您的某个 Mock 设置未按预期匹配(因为如果未找到匹配项,使用松散模拟时 Moq 将返回 default(T)),您可以暂时切换 MockBehaviour to Strict,这如果设置不匹配,将抛出。

例如将以下内容应用于TestGetUserBad

var mock = new Mock<IMyInterface>(MockBehavior.Strict);

结果:

Moq.MockException : IMyInterface.GetUserName(User) 调用失败,模拟行为严格。模拟上的所有调用都必须有相应的设置。

【讨论】:

    【解决方案2】:

    我已经通过添加以下代码解决了这个问题:

     userrepositoryMock.SetReturnsDefault(new User { Salt = strSalt, FundraiserAdminId = fundadmind, StatusCode = statusCode, UserTypeId = userTypeId, HomePageURL = homepageURL, OrganizationId = organizationId });
    

    【讨论】:

      猜你喜欢
      • 2016-01-28
      • 2013-11-23
      • 2023-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-24
      • 1970-01-01
      • 2019-01-28
      相关资源
      最近更新 更多