单元测试抽象类的受保护方法

时间:2013-12-10 20:19:20

标签: c# .net reflection nunit abstract-class

我正在尝试编写一个单元测试来测试抽象类中的受保护方法。我已经尝试编写一个继承自抽象类的测试类,但是当我实例化测试类时,基本抽象类尝试连接到Oracle数据库并失败,这使我无法测试我感兴趣的受保护方法in。抽象类不能修改。

如何在此抽象类中直接对受保护的方法进行单元测试?

以下是我尝试反思的片段。

Type type = typeof(AbstractClass);
BindingFlags eFlags = BindingFlags.Instance | BindingFlags.NonPublic;
MethodInfo myMethod = type.GetMethod("ProtectedMethod", eFlags);
object[] arguments = new object[] { _myDs };
myMethod.Invoke(type, arguments);
_myDs = (DataSet)arguments[0];

3 个答案:

答案 0 :(得分:3)

可行,但请注意您的代码可能存在几个严重问题:

  • 构造函数不应执行任何业务逻辑。
  • 您的抽象类不应该知道数据库类型(依赖性倒置原则)。
  • 你的抽象班有更多责任吗?如果是,请将它们移至单独的类/项目(单一责任原则)。
  • 您不应该对非公开API进行单元测试。

这是一个食谱:

  • 首先,创建一个抽象类的假实例。
  • 然后,调用FormatterServices.GetUninitializedObject获取类的实例而不调用构造函数。
  • 之后,您可以使用反射来调用ProtectedMethod

通过这种方式,您可以测试ProtectedMethod

  • 不调用构造函数。
  • 不修改抽象类。

这是一个示例代码:

class AbstractClassFake : AbstractClass { }

[Test]
public void Test()
{
    // Arrange:
    var abstractClassFake = (AbstractClassFake)FormatterServices
        .GetUninitializedObject(typeof(AbstractClassFake));

    MethodInfo method = abstractClassFake.GetType()
        .GetMethod("ProtectedMethod",
            BindingFlags.Instance | BindingFlags.NonPublic);

    object[] arguments = new object[] { myDs };

    // Act:
    object val = method.Invoke(abstractClassFake, new[] { myDs });

    // Assert:
    // TODO: Your assertion here
}

答案 1 :(得分:1)

妥协。

说你有:

public abstract class CanNeverChange
{
    public CanNeverChange()
    {
        //ACK!  connect to a DB!!  Oh No!!
    }

    protected abstract void ThisVaries();

    //other stuff
}

public class WantToTestThis : CanNeverChange
{
    protected override void ThisVaries()
    {
         //do something you want to test
    }
}

将其更改为:

public class WantToTestThis : CanNeverChange
{
    protected override void ThisVaries()
    {
         new TestableClass().DoSomethingYouWantToTest();
    }
}

public class TestableClass
{
    public void DoSomethingTestable()
    {
        //do something you want to test here instead, where you can test it
    }
}

现在,您可以在TestableClass中测试要测试的行为。对于WantToTestThis类,应对可怕遗留代码的压力,并且不测试它。使用最少量的未经测试的代码插入可测试的东西是一个历史悠久的策略;我第一次听到Michael Feather的书Working Effectively with Legacy Code

答案 2 :(得分:0)

我认为最好的解决方案就是在评论中提出要打破这个阶级。

然而,如果您想在不更改类的情况下测试它,可以将构造函数的内容移动到虚拟方法中:

public abstract class TheOracleOne
{
    protected virtual void Init()
    {
        // connection things
    }
}

在派生类中重写并不执行任何操作:

public class TestClass : TheOracleOne
{
    protected override void Init()
    {
    }
}

这应该跳过在抽象类的构造函数中发生的初始化,允许您在派生类上正常访问该方法。