2

我在单元测试中使用 MOQ 时遇到了一些奇怪的行为:

给定以下测试:

[Fact]
public void ShoppingCart_ShouldIncrementQuantity_WhenAddingDuplicateItem()
{
    var cart = new ShoppingCart();

    var item1 = GetMockItem("Test");
    var item2 = GetMockItem("Test", quantity: 2);

    cart.AddItem(item1.Object);
    cart.AddItem(item2.Object);

    cart.Items.Single(x => x.Sku == "Test").Quantity
        .Should().Be(3);
}

private Mock<IShoppingCartItem> GetMockItem(string sku, decimal price = 10, int quantity = 1)
{
    var mock = new Mock<IShoppingCartItem>();
    mock.Setup(x => x.Sku).Returns(sku);
    mock.Setup(x => x.Price).Returns(price);
    mock.Setup(x => x.Quantity).Returns(quantity);

    return mock;
}

这是正在测试的代码:

public void AddItem(IShoppingCartItem item)
{
    Enforce.ArgumentNotNull(item, "item");

    var existingItem = this.Items.SingleOrDefault(x => x.Sku == item.Sku);

    if (existingItem != null)
    {
        existingItem.Quantity += item.Quantity;
    }
    else
    {
        this.Items.Add(item);
    }
}

我得到这个结果:Test 'Titan.Tests.ShoppingCartTests.ShoppingCart_ShouldIncrementQuantity_WhenAddingDuplicateItem' failed: Expected 3, but found 1.

我很困惑,或者我只是有一个愚蠢的时刻!

4

2 回答 2

4

这里的问题是您没有告诉 Moq 在设置 Quantity 属性时要做什么。默认情况下,Moq 不只是假设您的所有属性都应该是简单的 getter/setter。由您决定如何处理它们。

你有几个选择。

使用 SetupAllProperties() 告诉 Moq 将属性视为简单的 getter/setter。

  private Mock<IShoppingCartItem> GetMockItem(string sku, decimal price = 10, int quantity = 1)
  {
        var mock = new Mock<IShoppingCartItem>();
        mock.SetupAllProperties();

        // Set the properties like normal properties. Moq will do the right thing.
        mock.Object.Sku = sku;
        mock.Object.Price = price;
        mock.Object.Quantity = quantity;
        return mock;
  }

使用 SetupSet 处理设置了 Quantity 属性的情况,并在其回调中重新设置属性 getter,使其返回新值。

  private Mock<IShoppingCartItem> GetMockItem(string sku, decimal price = 10, int quantity = 1)
  {
        var mock = new Mock<IShoppingCartItem>();
        mock.Setup(x => x.Sku).Returns(sku);
        mock.Setup(x => x.Price).Returns(price);
        mock.Setup(x => x.Quantity).Returns(quantity);

        // You can call Setups from within Setups
        mock.SetupSet(x => x.Quantity).Callback(q => mock.Setup(x => x.Quantity).Returns(q));
        return mock;
  }

或者,您也可以更改设计,以免修改公共属性。

于 2013-08-14T21:15:07.923 回答
1

第一项的模拟属性设置为始终返回 1。然后添加 2 并不重要,它将始终返回 1。

编辑:您的 += 被忽略,因为您的购物车存储了模拟对象。首先到达购物车的那个被嘲笑为总是返回 1。

于 2013-08-14T19:39:39.617 回答