【问题标题】:Mocking out the Data Accessor模拟数据访问器
【发布时间】:2016-09-09 02:14:13
【问题描述】:

我正在尝试了解如何将 Moq 与 NUnit 和 IoC 一起使用。

(我在 BitBucket 中有完整的项目,但不知道如何共享它...) https://bitbucket.org/Cralis/skeleton/overview

我有一个我正在尝试测试的逻辑方法(登录)。它接受一个请求对象(具有用户名、密码和 IP 地址)。如果用户名和/或密码为空,则逻辑返回失败状态,并且不会进入数据访问层。

所以我正在创建一个单元测试来测试它。 (这是我第一次尝试嘲笑......)

 public void NotNull_Returns_True()
        {
            // Arrange
            var request = new LoginRequest { IPAddress = "1.1.1.1", Username = "dummy", Password = "dummy" };
            var response = new LoginResponse { Request = request, Success = true, Message = "", UserID = 1 };

            // Setup the moc data accessor, as we don't want to gop to the concrete one.
            var MockedDataAccess = new Mock<IDataAccess>();

            // Set it's return value
            MockedDataAccess.Setup(x => x.Login(request)).Returns(response);

            // Instantiate the Logic class we're testing, using a Moc data accessor.
            var logic = new BusinessLogic(MockedDataAccess.Object);

            // Act
            var result = logic.Login(new LoginRequest { Password = "dummy", Username = "dummy", IPAddress = "1.1.1.1" });

            // Assert
            Assert.AreEqual(true, result.Success);
        }

断言失败,因为 'result' 为 NULL。

我可能做错了很多。例如,我不确定为什么需要在顶部设置请求和响应对象,但是因为我找到的所有示例都是“字符串”和“整数”输入,所以我似乎无法使用 It.IsAny。 ..

有人可以帮助我理解这里吗?在断言中获得 NULL 作为结果,我做错了什么?我逐步完成,代码按预期执行。但结果为空,因为我从未调用数据访问器(它使用了模拟)。

编辑: 啊,

// Set it's return value
            MockedDataAccess.Setup(x => x.Login(It.IsAny<LoginRequest>())).Returns(response);

这解决了问题。我不知道为什么,所以如果你能帮助我理解和重构它,让它像经验丰富的 Moq/UnitTester 期望的那样,那将非常有用。

【问题讨论】:

  • 您的Login 方法有什么作用?它是否调用 IDataAccess 依赖项上的方法,如果这些方法返回 null,则返回 null?
  • @BenRubin - 我添加了一个 Bitbucket 链接,因此您可以查看所有内容并进行克隆。但基本上,如果有空值发送到 BL 层,它会评估并返回一个成功标志为 False 的 Response 对象。它不应该返回一个空对象。

标签: c# unit-testing inversion-of-control moq


【解决方案1】:

即使您的request 对象与您传递给var result = logic.Login(new LoginRequest { Password = "dummy", Username = "dummy", IPAddress = "1.1.1.1" }); 的属性值相同,但它们是不同的对象,因此您尝试使用MockedDataAccess.Setup(x =&gt; x.Login(request)).Returns(response); 返回的值不会被返回.

改变

var result = logic.Login(new LoginRequest { Password = "dummy", Username = "dummy", IPAddress = "1.1.1.1" });

var result = logic.Login(request);

它与MockedDataAccess.Setup(x =&gt; x.Login(It.IsAny&lt;LoginRequest&gt;())).Returns(response); 一起工作的原因是因为现在你说“当MockedDataAccess.Login 被调用时,其参数的any 值,返回response

关于您问题的第二部分,您需要设置请求和响应对象的原因是,默认情况下,您在模拟对象上调用的任何方法都将返回 null。下面列出的BusinessLogic.Login 方法将返回dataAccess.Login() 的值。由于dataAccess 是一个模拟,dataAccess.Login() 方法将返回 null,除非您另有说明。

    public LoginResponse Login(LoginRequest request)
    {

        // Basic validation
        if (request == null)
            return new LoginResponse
            {
                Success = false,
                Message = "Empty Request"
            };

        if (string.IsNullOrEmpty(request.Username) || string.IsNullOrEmpty(request.Password))
            return new LoginResponse
            {
                Success = false,
                Message = "Username and/or password empty"
            };

        // This is returning null since dataAccess is a mock
        return dataAccess.Login(request);
    }

您说您认为自己做错了很多,但是您设置测试的方式几乎就是我所做的。我唯一要改变的(除了如上所述修复Setup 方法)是为您的测试使用UnitOfWork_StateUnderTest_ExpectedBehavior 命名模式。例如Login_ValidLoginRequest_ShouldReturnValidLoginResponse()

【讨论】:

  • 谢谢@BenRubin - 这一切都很有意义,帮助我走上学习之路,并提供了一些验证。谢谢。
【解决方案2】:

这段代码的问题

var result = logic.Login(new LoginRequest { Password = "dummy", Username = "dummy", IPAddress = "1.1.1.1" })

是在Login方法的实现中调用了这个

dataAccess.Login(request)

这意味着您必须为方法Login 设置DataAccess 的模拟,因为模拟没有其他任何作用。模拟 fake 并且需要进行设置,以便按照您需要的方式工作。在这种情况下,@Ben Rubin 的答案是绝对正确的。

当这样设置模拟时

MockedDataAccess.Setup(x =&gt; x.Login(request)).Returns(response)

那么有必要使用与设置数据访问Login方法中使用的请求完全相同的请求对象调用被测方法,否则mock将充当未设置。在这里,您基本上是在说'当使用此请求调用 DataAccess Login 时,将返回该响应'。

但是当像这样设置模拟时

MockedDataAccess.Setup(x =&gt; x.Login(It.IsAny&lt;LoginRequest&gt;())).Returns(response)

然后它可以工作,因为这里Login 设置为任何LoginRequest。所以在这种情况下,无论使用什么请求,模拟都会返回response。高温

这里有更多关于Mock Matching Arguments的信息

【讨论】:

    猜你喜欢
    • 2023-04-02
    • 2011-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-17
    • 1970-01-01
    • 1970-01-01
    • 2015-02-08
    相关资源
    最近更新 更多