【问题标题】:Is it possible to use a mocked object as an input for another method up for mocking?是否可以使用模拟对象作为另一种模拟方法的输入?
【发布时间】:2019-05-30 14:50:32
【问题描述】:

我创建了一个先前的测试方法,在单个数据访问上设置了两个模拟对象,它工作正常。是否有另一个具有相同场景但结果失败。

这是测试方法:

[Test]
public void UpdateUserPassword_WhenInputsAreCorrect_ReturnsQuerySuccessMessage()
    {
        UpdatePasswordModel input = new UpdatePasswordModel()
        {
            UserName = "john.doe",
            NewPassword = "password1", //password1
            PreviousPassword = "password" //password
        };

        Mock<IUserDataAccess> user = new Mock<IUserDataAccess>();
        Mock<IDailyTimeInDataAccess> timeIn = new Mock<IDailyTimeInDataAccess>();
        Mock<IDailyTimeOutDataAccess> timeOut = new Mock<IDailyTimeOutDataAccess>();

        user.Setup(x => x.UpdatePassword(10000, input.NewPassword)).Returns("User record updated.");
        user.Setup(x => x.GetUser(input.UserName)).Returns(new User()
        {
            UserKey = 10000,
            UserName = "john.doe",
            UserPassword = "LTg9BIob8urwz643K5+pBA=="
        });

        ILoginBusinessRules app = new LoginBusinessRules(user.Object, timeIn.Object, timeOut.Object);

        var output = app.UpdateUserPassword(input);

        Assert.AreEqual("User record updated.", output);
    }

这是业务规则:

public string UpdateUserPassword(UpdatePasswordModel model)
    {
        if (model == null)
        {
            return "No data to process.";
        }
        if (string.IsNullOrEmpty(model.UserName))
        {
            return "Username is empty.";
        }
        if (string.IsNullOrEmpty(model.NewPassword))
        {
            return "New Password is empty.";
        }
        if (string.IsNullOrEmpty(model.PreviousPassword))
        {
            return "Previous Password is empty.";
        }

        var user = _userDataAccess.GetUser(model.UserName);
        if (user == null)
        {
            return "User not found.";
        }

        if (user.UserPassword != EncryptPassword(model.PreviousPassword))
        {
            return "Previous password does not match.";
        }
        else
        {
            user.UserPassword = EncryptPassword(model.NewPassword);
            user.UpdateDttm = DateTime.Now;
            user.UpdateUserId = model.UserName;

            var result = _userDataAccess.UpdatePassword(user.UserKey, user.UserPassword);

            return result;
        }
    }

测试返回失败。进一步调试告诉我,这里的这一行返回 null:

var result = _userDataAccess.UpdatePassword(user.UserKey, user.UserPassword);

非常感谢任何帮助!

【问题讨论】:

    标签: c# tdd moq


    【解决方案1】:

    要回答帖子中的问题,是的,您可以使用与输入类型匹配的任何对象。您的代码并不真正了解“模拟”和“真实”对象之间的区别。

    【讨论】:

      【解决方案2】:

      Setup 使用 input.NewPassword,测试结果是

      UpdatePasswordModel input = new UpdatePasswordModel() {
          //...
          NewPassword = "password1",
          //...
      };
      
      //...
      
      user.Setup(x => x.UpdatePassword(10000, input.NewPassword)).Returns("User record updated.");
      
      //...
      

      但在被测方法中,该方法是用另一个值调用的

      //...
      
      user.UserPassword = EncryptPassword(model.NewPassword);
      
      //...
      
      var result = _userDataAccess.UpdatePassword(user.UserKey, user.UserPassword);
      

      这与设置中的预期不匹配。

      当一个模拟成员没有被预期调用时,它将返回返回类型的默认值,在这种情况下为 null

      您需要确保在设置期望中使用正确的值

      例如

      user
          .Setup(x => x.UpdatePassword(10000, EncryptPassword(input.NewPassword)))
          .Returns("User record updated.");
      

      或者使用像It.IsAny&lt;T&gt;()这样的参数匹配器来放松设置的期望

         user
          .Setup(x => x.UpdatePassword(10000, It.IsAny<string>()))
          .Returns("User record updated.");
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-05
        相关资源
        最近更新 更多