如何使用列表来存储模拟对象

本文关键字:模拟 对象 存储 何使用 列表 | 更新日期: 2023-09-27 18:34:19

我是这个Moq社区的新人。

我想知道有没有办法将 A(模拟(的继承对象放入模拟接口 A 的列表?

例如,假设我们有

Class ClientA :IMyClient{...}   //has override virtual codes
Class ClientB :IMyClient{...}

 List<Mock<IMyClient>> mockClients = new List<Mock<IMyClient>>();

无论如何,我可以做这样的事情:

var mockClientA = new Mock<ClientA>();
mockClients.Add(mockClientA);

我可能没有走在正确的轨道上。需要一些帮助。谢谢。

====

==================================

设置

   // setup everyone to Free
    actualManager.Status = Status.Free;
    foreach (var mock in mockClients) {
        mock.Object.Status = Status.Free;
        actualManager.AddClient(mock.Object);
    }

测试

    Assert.AreEqual(Status.Free, actualManager.Status); // pass
    var firstClient = mockClients.First();
    IMyClient actualFirstClient  = SetClientToTriggerOutsideCallbacks(firstClient);
    firstClient.Status = Status.Busy;
    // busy, since the first client is busy now.
    Assert.AreEqual(Status.Busy, actualManager.Status);  //pass
    SessionSwitchEventArgs e = new SessionSwitchEventArgs(SessionSwitchReason.SessionLock);
    actualManager.SystemEvents_SessionSwitch(null, e);
    Assert.AreEqual(Status.Away, actualManager.Status);   //not pass

我试图检查状态更改对不同客户端的影响,所以我试图使用列表来存储所有客户端。问题是:如果第一个客户端不是 ClientA 类型,则actualManager.SystemEvents_SessionSwitch(null, e);无法正常工作。

如何使用列表来存储模拟对象

首先 - 为什么我们需要模拟和存根?因为我们的 SUT(被测系统,您正在测试的对象(通常有一些依赖关系。我们不能使用生产代码中真正的依赖项,因为我们将无法判断测试通过或失败的原因。这可能是 SUT 或依赖项中的错误。因此,我们应该提供一些东西,而不是真正的依赖,这将是可靠的。也就是说,无论您如何更改实际依赖项的实现,它都不应该产生任何意外错误。这就是我们使用存根和模拟的原因。

但是它们之间有什么区别呢?存根用于执行状态验证的测试。即,当您在行使 SUT 后验证 SUT 的状态和(有时(存根时。存根可以非常简单。您可以只实现依赖项接口并使用自动属性,而无需内部任何逻辑。设置一些值就足够了,SUT 会将某些内容读/写到存根中。最后,您将检查状态。您将不会检查执行了哪些操作。

行为验证是非常不同的。它侧重于 SUT 和依赖项之间的交互。我们为模拟对象设置了一些期望。这与使用虚假数据设置某些属性不同。使用 mock,您可以设置应该调用属性或方法的事实,并且应该传递正确的参数。使用存根,您不会检查它是否被调用。您只需使调用成为可能并在最后检查状态即可。

所以..你在这里做的是用模拟进行状态验证。这不是应该使用模拟的方式。即您设置客户端的状态,然后检查管理器的状态。而不是使用最小起订量,以下简单的实现完全满足您的需求:

public class ClientStub : IMyClient
{
    public Status Status { get; set; }
    // some other members here
}

您可以设置状态。经理可以阅读它。您可以检查经理的状态。如果你打算使用模拟,你应该使用行为验证(否则只是矫枉过正(。很难说出经理类的目的是什么,以及它如何检查客户端的状态。如果您添加此信息,我将为您的经理添加一些行为验证测试。

关于您关于将不同类型的模拟放入列表的问题 - 你不能这样做(好吧,只有当你有对象列表时(。这毫无意义。如果某些 SUT 需要依赖项列表,则应将模拟对象(可通过 mock 的属性访问Object(放入该列表中。这就是Moq和RhinoMocks之间的区别。

谢谢你,@SergeyBerezovskiy。我甚至没有朝着正确的方向前进。感谢您的参考链接。我在 C# 版本中重写了链接示例并进行了测试。我认为它帮助我理解了很多 Mock。如果我在这里做了什么不好的事情,请告诉我。谢谢。

仓库.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Test {
    public class WareHouse {
    private Dictionary<string, int> wareHouseRepo = new Dictionary<string, int>();
    public void Add(string location, int number) {
    //null add
    if (wareHouseRepo.ContainsKey(location)) {
    int inventory_Old = wareHouseRepo[location];
        wareHouseRepo[location] = inventory_Old + number;
    } else {
         wareHouseRepo.Add(location, number);
    }
}

public virtual bool FillIt(string location, int number) {
    if (wareHouseRepo.ContainsKey(location)) {
        int inventory = wareHouseRepo[location];
        if(inventory >= number){
            wareHouseRepo[location] = inventory - number;
            return true;
        }else{
            return false;
        }
    } else {
        return false;
    }
}
}    
public int GetInventory(string location) {
    if (wareHouseRepo.ContainsKey(location)) {
        return wareHouseRepo[location];              
    } else {
        return 0;
    }
    }
}

序.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
    namespace Test {
        class Order {
            private string location = "";
            private int orderNum = 0;
            private bool filled = true;
            public Order(string loc, int num) {
                this.location = loc;
                this.orderNum = num;
            }
            public void Fill(WareHouse wh){
                if (wh.FillIt(location, orderNum)) {
                    filled = true;
                } else {
                    filled = false;
                }
            }
            public bool isFilled() {
                return filled;
            }
        }
    }

测试仓库.cs

using Moq;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Test {
    [TestFixture]
    class TestWareHouse {
        private WareHouse warehouse = new WareHouse();
        private static String TALISKER = "Talisker";
        private static String HIGHLAND_PARK = "Highland Park";
        [SetUp]
        public void SetUp() {
            //reset
            warehouse = new WareHouse();
            warehouse.Add(TALISKER, 50);
            warehouse.Add(HIGHLAND_PARK, 25);
        }
        //regular testing
        [Test]
        public void testOrderIsFilledIfEnoughInWarehouse() {
            Order order = new Order(TALISKER, 50);
            order.Fill(warehouse);
            Assert.True(order.isFilled());
            Assert.AreEqual(0, warehouse.GetInventory(TALISKER));
        }
         [Test]
        public void testOrderDoesNotRemoveIfNotEnough() {
            Order order = new Order(TALISKER, 51);
            order.Fill(warehouse);
            Assert.False(order.isFilled());
            Assert.AreEqual(50, warehouse.GetInventory(TALISKER));
        }

        //Now I am trying to do the things with Mock
        [Test]
        public void testOrderIsFilledIfEnoughInWarehouseMock() {
            Order order = new Order(TALISKER, 50);
            //-- Creating a fake ICustomerRepository object
            var warehouseMock = new Mock<WareHouse>();
            //warehouseMock
            warehouseMock
            .Setup(m => m.FillIt(TALISKER, 50))
            .Returns(true);
            order.Fill(warehouseMock.Object);

            //-- Assert ----------------------
            Assert.IsTrue(order.isFilled());

            warehouseMock.Verify(x => x.FillIt(It.IsAny<string>(), It.IsAny<int>()), Times.Exactly(1));
            }
        [Test]
        public void testFillingDoesNotRemoveIfNotEnoughInStock() {
            Order order = new Order(TALISKER, 51);
            //-- Creating a fake ICustomerRepository object
            var warehouseMock = new Mock<WareHouse>();
             warehouseMock
            .Setup(m => m.FillIt(It.IsAny<string>(), It.IsAny<int>()))
            .Returns(false);
             order.Fill(warehouseMock.Object);

            //-- Assert ----------------------
            Assert.IsFalse(order.isFilled());
            warehouseMock.Verify(x => x.FillIt(It.IsAny<string>(), It.IsAny<int>()), Times.Exactly(1));
        }
      }
}