我试图模拟(使用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
这也是可以理解的,因为Type
类上的Vehicle
属性不是虚拟的。
所以我的问题是:在模拟Type
类时有没有办法设置Jeep
属性?
答案 0 :(得分:2)
在这种情况下,最好的Moq可以创建一个代理作为Jeep
的派生类,但它不能覆盖非虚拟Type
属性。请记住,当您尝试使用Moq创建Mock时,框架会生成一个实现目标接口或从目标类派生的类。
Microsoft Fakes与Moq配合使用,前提是您拥有Visual Studio许可级别来访问它。
答案 1 :(得分:1)
由于这是一个相当普遍的问题,我将提出一个不同的方法。模拟/伪装/存取您无法控制的类或接口通常很困难。此外,这通常会导致在模拟实例中复制其他人的功能。
更好的方法是将与外部库的交互隔离到一个包装类中,您可以控制它的接口和实现。这将允许您轻松模拟/伪造/存储您的界面,以测试您的界面的消费者。
这仍然留下了如何处理隔离层类的问题。为此,您需要进行更多的集成测试,该测试实际上会运行您的包装类和外部类。然后,验证您是否从您的类和正在包装的外部类的组合中获得了预期的行为。
答案 2 :(得分:0)
所以我的问题是:在模拟Jeep类时有没有办法设置Type属性?
简短的回答是否定的,你不能使用Moq模拟非虚拟成员。
但是,我不认为在这种情况下有任何理由嘲笑任何事情,因为没有任何行为可以嘲笑。您拥有的唯一成员是一个简单的string
属性。所以,即使你可以,你可能也不应该。
Jeep
类,请直接测试它
不嘲笑其任何成员;只有在需要控制被测系统(SUT)的某个依赖关系的行为时才应使用模拟Jeep
为了测试该方法,可以将另一个方法作为依赖的类来实现
new
Jeep
个IVehicle
个实例,并在此案例中将其传递给您;因为它没有
那里有任何过境行为(网络电话等)
没有必要嘲笑它。您可能还会考虑,如果它是第二种情况,您应该能够传递Jeep
而不是Vehicle
,在这种情况下,模拟会回到桌面上(虽然如上所述,在这种情况下没有明显的需要)
顺便提一下,您的层次结构看起来有点偏离 - 为什么IVehicle
本身不会实现public interface IVehicle
{
string Type { get; }
}
public class Vehicle: IVehicle
{
public string Type { get; }
}
public class Jeep : Vehicle
{
}
?
即
Jeep
然后IVehicle
已经既是车辆又是{{1}}:)
答案 3 :(得分:0)
免责声明:我在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 property,测试看起来像这样
var jeep = Isolate.Fake.Instance<Jeep>();
jeep.Type = "fake jeep";