简体   繁体   English

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

[英]Unit Test protected method of an abstract class

I'm trying to write a unit test to test a protected method in an abstract class. 我正在尝试编写单元测试来测试抽象类中的受保护方法。 I've tried writing a test class that inherits from the abstract class, but when I instantiate the test class the base abstract class attempts to connect to an Oracle database and fails which doesn't allow me to test the protected method I'm interested in. The abstract class cannot be modified. 我已经尝试编写一个继承自抽象类的测试类,但是当我实例化测试类时,基本抽象类尝试连接到Oracle数据库并失败,这使我无法测试我感兴趣的受保护方法in。抽象类不能修改。

How can I directly unit test a protected method in this abstract class? 如何在此抽象类中直接对受保护的方法进行单元测试?

Here is snippet of what I tried with reflection. 这是我尝试反思的片段。

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];

It is doable , but please note there may be couple of serious problems with your code: 它是可行的 ,但请注意您的代码可能有几个严重的问题:

  • Constructor shouldn't perform any business logic. 构造函数不应执行任何业务逻辑。
  • Your abstract class should not know about database type (Dependency Inversion Principle). 您的抽象类不应该知道数据库类型(依赖性倒置原则)。
  • Is your abstract class have more responsibilities? 你的抽象班有更多的责任吗? If yes, move them to separate classes / projects (Single Responsibility Principle). 如果是,请将它们移至单独的类/项目(单一责任原则)。
  • You shouldn't unit test non-public API. 您不应该对非公共API进行单元测试。

Here's a recipe: 这是一个食谱:

  • First, create a fake instance of your abstract class. 首先,创建一个抽象类的假实例。
  • Then, call FormatterServices.GetUninitializedObject for getting instance of the class without calling constructor. 然后,调用FormatterServices.GetUninitializedObject来获取类的实例而不调用构造函数。
  • After that, you can use reflection for invoking ProtectedMethod . 之后,您可以使用反射来调用ProtectedMethod

By that, you are able to test ProtectedMethod : 通过这种方式,您可以测试ProtectedMethod

  • Without calling constructor. 不调用构造函数。
  • Without modifying abstract class. 不修改抽象类。

And here is an example code: 这是一个示例代码:

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
}

Compromise. 妥协。

Say you have: 说你有:

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
    }
}

Change it to this: 把它改成这个:

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
    }
}

Now you can test the behavior you want to test in TestableClass . 现在,您可以在TestableClassTestableClass要测试的行为。 For the WantToTestThis class, compromise to the pressure of terrible legacy code, and don't test it. 对于WantToTestThis类,妥协于可怕遗留代码的压力,并且不测试它。 Plugging in a testable thing with a minimal amount of untested code is a time honored strategy; 使用最少量的未经测试的代码插入可测试的东西是一个历史悠久的策略; I first heard of it from Michael Feather's book Working Effectively with Legacy Code . 我第一次听到Michael Feather的书“有效地使用遗留代码”一书。

I think the best solution is to break the class apart as the comments suggest. 我认为最好的解决方案就是在评论中提出要打破这个阶级。

However , if you would like to test it without changing the class very much, you could move the contents of the constructor into a virtual method: 但是 ,如果您想在不更改类的情况下测试它,可以将构造函数的内容移动到虚拟方法中:

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

And override in your derived class to do nothing: 并在派生类中覆盖以执行任何操作:

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

This should skip the initialization that is taking place in the abstract class's constructor, allowing you to access the method normally on the derived class. 这应该跳过在抽象类的构造函数中发生的初始化,允许您在派生类上正常访问该方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM