如何在C#中模拟没有接口且没有虚拟方法的类?

时间:2016-09-06 19:50:42

标签: c# unit-testing

我正在为别人的代码编写单元测试,我不允许修改。

说我有:

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%以下的覆盖率。

3 个答案:

答案 0 :(得分:6)

  

考虑到我无法修改我正在测试的代码,是否有   直截了当的方式使这项工作?

没有。但是,我看到两个非直截了当的选择:

A)使用Microsoft Fakes通过重写库来提供模拟。您不必修改代码。示例:您可以修改静态 DateTime.Now 以返回您选择的日期,如下所示:

System.Fakes.ShimDateTime.NowGet = 
() =>
{ return new DateTime(fixedYear, 1, 1); };

B)您可以尝试继承测试的类并覆盖使用的方法/ ctors。但是有两个限制。您必须能够使用模拟对象实例化/交换测试类的实例(通过使用反射,您可以访问私有成员),并且模拟方法必须是虚拟的。在您的情况下,您将创建:

class BadClassMock : BadClass
{
    public BadClassMock()
    {
    }

    public bool someFlag;

    public void InitForTest()
    {
        someFlag = true;
    }
}

只要你不调用基础构造函数,它就可以工作。你可以从这里使用令人讨厌的 FormatterServices.GetUninitializedObject()技巧:Creating instance of type without default constructor in C# using reflection

以后:

public void AnotherBadClassConstructorTest()
{
    BadClassMock badClassMock = (BadClassMock)FormatterServices.GetUninitializedObject(typeof(BadClassMock));
    badClassMock.InitForTest();
    AnotherBadClass anotherBadClass = new AnotherBadClass(badClassMock);
    Assert.IsNotNull(anotherBadClass);
}

是的,这是一个非常大的解决方法。显然,最好的方法是说服作者修改代码。

答案 1 :(得分:0)

实际上,您可以在不修改代码的情况下模拟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。

答案 2 :(得分:0)

如果您不想更改接口中的代码,则需要使用TypeMockJustMock进行模拟。两者都是付费工具,我还没有找到一个可以做你想做的免费工具。我更喜欢JustMock。 JustMock的语法如下所示:

public void AnotherBadClassConstructorTest()
{
    BadClassMock badClassMock = Mock.Create<BadClassMock>();
    AnotherBadClass anotherBadClass = new AnotherBadClass(badClassMock);
    Assert.IsNotNull(anotherBadClass);
}

如果你要做很多事情或者想要测试那些没有用单元测试编写的遗留代码,那么这些工具在我看来是非常值得的。您也可以随时进行免费试用。