如何使用列表来存储模拟对象
本文关键字:模拟 对象 存储 何使用 列表 | 更新日期: 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));
}
}
}