如何在模拟具体类时设置属性
本文关键字:设置 属性 模拟 | 更新日期: 2023-09-27 18:03:06
我试图模拟(使用Moq)在第三方SDK中定义的类和接口。下面是它们的简化示例:
public interface IVehicle
{
string Type { get; }
}
public class Vehicle
{
public string Type { get; }
}
public class Jeep : Vehicle, IVehicle
{
}
我可以很容易地模仿这个界面,像这样:
var mockVehicle = new Mock<IVehicule>(MockBehavior.Strict);
mockVehicle
.SetupGet(v => v.Type)
.Returns("My custom vehicle");
但是我需要写一个特定于Jeep
类的单元测试,我不知道如何模拟它。
我的第一次尝试:
var mockJeep = new Mock<Jeep>(MockBehavior.Strict);
产生以下错误:
Moq.MockException: IVehicle.Type invocation failed with mock behavior Strict.
All invocations on the mock must have a corresponding setup.
这是可以理解的,因为我没有设置Type
属性。
第二次尝试:
var mockJeep = new Mock<Jeep>(MockBehavior.Strict);
mockJeep
.SetupGet(v => v.Type)
.Returns("Jeep");
产生以下错误消息:
System.NotSupportedException: Invalid setup on a non-virtual (overridable in VB) member: v => v.Type
这也是可以理解的,因为Vehicle
类上的Type
属性不是虚拟的。
所以我的问题是:有没有办法设置Type
属性时嘲笑Jeep
类?
在这种情况下,Moq可以做的最好的事情是创建一个代理作为Jeep
的派生类,但它不能覆盖非虚拟的Type
属性。请记住,当您尝试使用Moq创建Mock时,框架会生成一个类,该类要么实现目标接口,要么从目标类派生。
Microsoft Fakes可以很好地与Moq一起工作,前提是您拥有Visual Studio许可级别来访问它。
由于这是一个相当一般化的问题,我将建议采用不同的方法。mock/fake/stub一个你无法控制的类或接口通常是困难的。此外,这通常会导致在模拟实例中复制其他人的功能。
更好的方法是将与外部库的交互隔离到一个包装类中,由您控制接口和实现。这将允许您轻松地mock/fake/stub 您的接口,以测试接口的消费者。
这仍然留下了如何处理隔离层类的问题。为此,您将需要进行更多的集成测试,该测试实际上将同时练习包装类和外部类。然后,验证您从您的类和您包装的外部类的组合中获得了预期的行为。
所以我的问题是:有没有一种方法来设置类型属性时嘲笑吉普类?
简短的回答是否定的,你不能使用Moq来Mock非虚拟成员。
然而,我不认为在这种情况下有任何理由去嘲笑任何东西,因为没有任何行为可以嘲笑。这里唯一的成员是一个简单的string
属性。所以,即使你可以,你可能不应该。
- 如果你想测试
Jeep
类,直接测试它不嘲笑任何成员;mock应该只在您需要控制被测系统(System Under Test, SUT) 的一个依赖项的行为时使用。 - 如果通过
Jeep
类绑定到另一个方法作为依赖项,以便测试该方法,您可以new
启动一个Jeep
实例,并在测试中通过它;因为它没有有任何跨界行为(网络调用等),然后有吗没有必要嘲笑它。
您还可以考虑,如果是第二种情况,您应该能够传递IVehicle
而不是Jeep
,在这种情况下,mock将重新出现(尽管如上所述,在这种情况下没有明显的需要)
顺便说一下,你的层次结构看起来有点偏离-为什么Vehicle
本身不实现IVehicle
?
。
public interface IVehicle
{
string Type { get; }
}
public class Vehicle: IVehicle
{
public string Type { get; }
}
public class Jeep : Vehicle
{
}
那么Jeep
将已经是一个车辆和IVehicle
:)
免责声明:我在Typemock工作。
通过使用Typemock Isolator,您将能够模拟非虚拟方法,并且不需要为此更改代码,在这个特定的示例中,您可以伪造Jeep
的实例,然后修改其方法行为。
下面是一个模拟Jeep
的测试示例:
[TestMethod]
public void CallFakeJeepType_WillReturnWantedString()
{
var jeep = Isolate.Fake.Instance<Jeep>();
Isolate.WhenCalled(() => jeep.Type).WillReturn("fake jeep");
var result = Jeep.DoSomthing(jeep);
Assert.AreEqual("fake jeep", result);
}
注意:如果Jeep.Type
也有一个setter,你可以使用typemock的True属性,测试将看起来像这样
var jeep = Isolate.Fake.Instance<Jeep>();
jeep.Type = "fake jeep";