如何模拟c#中没有接口和虚方法的类
本文关键字:接口 方法 何模拟 模拟 | 更新日期: 2023-09-27 18:06:01
我正在为别人的代码编写单元测试,我不允许修改。
假设我有:
class BadClass
{
public BadClass()
{
// the service isn't going to be running during testing
// it also has no interface
someFlag = AGloballyStoredService.getSomeFlag();
}
public bool someFlag;
}
:
class AnotherBadClass
{
public AnotherBadClass(BadClass badClass)
{
someFlag = badClass.someFlag;
}
public bool someFlag;
}
说我想要测试:
public void AnotherBadClassConstructorTest()
{
BadClass badClass = new BadClass();
AnotherBadClass anotherBadClass = new AnotherBadClass(badClass);
Assert.IsNotNull(anotherBadClass);
}
我想模拟BadClass,但它没有接口,如果服务没有运行,它在构造函数期间会失败。
考虑到我不能修改我正在测试的代码,是否有一种直接的方法使其工作?
如果归结到这一点,我可以告诉他们,他们要么让我修改现有的代码库,要么接受这个模块低于10%的覆盖率。
考虑到我不能修改我正在测试的代码,是否存在让它工作的直接方法是什么?
。但是,我看到了两个不直接的选项:
A)使用Microsoft Fakes通过重写库来提供mock。你不需要修改代码。例如:可以修改static DateTime。现在返回您选择的日期,像这样:System.Fakes.ShimDateTime.NowGet =
() =>
{ return new DateTime(fixedYear, 1, 1); };
B)你可以尝试继承被测试的类并覆盖使用的方法/函数。但是有两个限制。您必须能够用模拟对象实例化/交换被测试类的实例(通过使用反射,您可以获得私有成员),并且模拟方法必须是虚拟的。在您的示例中,您将创建:
class BadClassMock : BadClass
{
public BadClassMock()
{
}
public bool someFlag;
public void InitForTest()
{
someFlag = true;
}
}
只要不调用基构造函数,它就可以工作。你可以从这里使用nasty FormatterServices.GetUninitializedObject()技巧:在c#中使用反射
创建没有默认构造函数的类型实例,后来:
public void AnotherBadClassConstructorTest()
{
BadClassMock badClassMock = (BadClassMock)FormatterServices.GetUninitializedObject(typeof(BadClassMock));
badClassMock.InitForTest();
AnotherBadClass anotherBadClass = new AnotherBadClass(badClassMock);
Assert.IsNotNull(anotherBadClass);
}
是的,这是一个相当大的变通方法。显然,最好的方法是说服作者修改代码。
如果您不想更改代码以接受接口,您将需要使用TypeMock或JustMock来模拟这一点。两者都是付费的工具,我还没有找到一个免费的工具,可以做你想做的事情。我更喜欢JustMock。JustMock的语法如下所示:
public void AnotherBadClassConstructorTest()
{
BadClassMock badClassMock = Mock.Create<BadClassMock>();
AnotherBadClass anotherBadClass = new AnotherBadClass(badClassMock);
Assert.IsNotNull(anotherBadClass);
}
如果你要经常这样做,或者想要测试没有使用单元测试编写的遗留代码,这些工具在我看来是值得的。你也可以免费试用一下
实际上,您可以在不修改代码的情况下模拟BadClass
,通过使用TypeMock Isolator,您将能够模拟类,即使它不是虚拟的或接口。例如,下面是您尝试创建的测试:
[TestMethod]
public void CreateAnotherBadClass_IsNotNull()
{
var fakeBadClass = Isolate.Fake.Instance<BadClass>();
var anotherBadClass = new AnotherBadClass(fakeBadClass);
Assert.IsNotNull(anotherBadClass);
}
在这个测试中,我创建了一个假的BadClass
实例,并将其作为参数发送给AnotherBadClass
c'tor。