【问题标题】:Testing a specific implementation with Moq使用 Moq 测试特定实施
【发布时间】:2012-06-06 19:47:46
【问题描述】:

更新 1,更多细节

我的问题很难解释,抱歉....

简单的问题... 您如何使用 Moq 测试特定实现?

interface IShipping
{
    int CalculateCost();
}

class TruckShipping: IShipping
{
    int CalculateCost()
    {
        return 9;
    }
}

class Shipping: IShipping
{
    int CalculateCost()
    {
        return 5;
    }
}

class CalculateSomethingMore
{
    IShipping _shipping;
    CalculateSomethingMore(IShipping shipping)
    {
        // Here I want the TruckShipping implementation!
        _shipping = shipping;
    }
    DoCalc()
    {
        return _shipping.CalculateCost * 2;
    }
 }

如果没有模拟,它可能看起来像(如果你不使用 DI) 测试:

var truckShipping = new TruckShipping();
var advancedCalculation = CalculateSomethingMore(truckShipping);
var result = DoCalc();

var expected = 18;
Assert.IsTrue(result == expected);

NUnit、FluentAssertions、MbUnit、xUnit 等都无所谓 :)

测试: var truckShipping = Mock.Of .... ?我想测试 TruckShipping 的实现。

并将其注入CalculateSomethingMore。

【问题讨论】:

    标签: c# unit-testing moq


    【解决方案1】:

    如果您想测试 TruckShipping 实施,那么您需要单独测试 TruckShipping 实施。

    目前您正在测试CalculateSomethingMore 的行为,它应该返回两倍的成本值,按运费计算。你不在乎哪个运输。 CalculateSomethingMore 的职责是询问运费的成本,并将成本加倍:

    Mock<IShipping> shipping = new Mock<IShipping>();
    shipping.Setup(s => s.CalculateCost()).Returns(10);
    CalculateSomethingMore sut = new CalculateSomethingMore(shipping.Object);
    Assert.That(20, Is.EqualTo(sut.DoCalc()));
    shipping.VerifyAll();
    

    您可以看到,该测试既没有使用9 也没有使用5 作为运费。为什么?因为你实际上并不关心它会有什么价值。计算运费不是被测班级的责任。

    您还需要对您的 TruckShipping 实施进行另一次测试,这将验证 TruckShipping 正确计算运费(这是您的运输对象的责任):

    TruckShipping sut = new TruckShipping();
    Assert.That(9, Is.EqualTo(sut.DoCalc());
    

    【讨论】:

    • 感谢您的澄清,这有助于我理解 Mocking 和不同的实现(没关系),因为最全面的答案,我将其标记为已接受。
    • @Janus007 不客气 :) 顺便说一句,好的做法是在您的测试(您正在测试的)中只有一个真实的对象。因此,如果某些依赖项的实现发生变化,您的测试将不会失败。当您期望实际值为真时,您也可以简单地使用Assert.IsTrue(result)
    【解决方案2】:

    Moq 不是一个测试框架,它是一个 mockinq 框架,所以它不提供测试功能。

    为了测试你可以使用SharpTestEx,例如你可以写:

    [TestMethod]
    public void VerifyCostIsCorrect()
    {
        new TruckShipping()
            .CalculateCost()
            .Should("The TruckShipping implementation returns a wrong cost.").Be.EqualTo(9);
    }
    

    【讨论】:

    • 那不是我正在测试的模拟对象,为什么你省略了模拟?查看我更新的问题:)
    • 你想测试 TruckShipping 的实现,那么,为什么要创建一个 mock?测试模拟对象没有意义,需要 mockinq 来设计问题而不用担心实现。
    • 嗨,Matteo,我的问题没有更新...请立即查看。
    • 对不起,我不明白你想做什么。你想测试 TruckShipping,那么,为什么要使用模拟?或者只是您想要一个使用 Moq 而不是 TruckShipping 类的示例?
    【解决方案3】:

    您使用模拟替换真正的依赖项(通常太复杂而无法实例化)用伪造的轻量级对象。测试模拟对象毫无意义,因为本质上,您将测试来自某个 3rd 方库的自动生成代码。

    编辑

    目前还不清楚您为什么要依赖具体实现,尤其是考虑到您正在使用 IoC 并注入 IShipping。无论如何,我认为您想要可以模拟TruckShipping 的东西,而不是TruckShipping 本身。如果是这样,那很简单:

    var shippingMock = new Mock<IShipping>();
    // setting up mock to return 9 makes it behave as if it was TruckShipping
    shippingMock.Stub(s => s.CalculateCost()).Returns(9);
    var advancedCalculation = CalculateSomethingMore(shippingMock.Object);
    
    var result = DoCalc();
    
    var expected = 18;
    Assert.IsTrue(result == expected);
    

    【讨论】:

    • 我相信你读错了我的问题......这不是我要测试的实际模拟对象,而是使用模拟对象的类。请查看我更新的问题:)
    • @Janus007:您很少在测试中使用具体实现,尤其是在有可用的模拟框架的情况下。 Mocks 为您提供了可以模拟那些具体实现的东西,同时又是其他东西。查看我的编辑,让我知道这是否是您一直在寻找的。​​span>
    • 啊,我想我现在明白了,我想我应该专注于测试特定的实现,而实际上应该使用相关值设置模拟。感谢您的耐心等待。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-01
    • 1970-01-01
    • 2023-01-17
    • 1970-01-01
    • 1970-01-01
    • 2013-11-03
    • 2014-01-02
    相关资源
    最近更新 更多