Unit testing 具有许多私有方法的单元测试复杂类

Unit testing 具有许多私有方法的单元测试复杂类,unit-testing,testing,dependency-injection,mocking,separation-of-concerns,Unit Testing,Testing,Dependency Injection,Mocking,Separation Of Concerns,我有一个类,其中有一个公共方法和许多私有方法,它们的运行取决于传递给公共方法的参数,因此我的代码如下所示: public class SomeComplexClass { IRepository _repository; public SomeComplexClass() this(new Repository()) { } public SomeComplexClass(IRepository repository) {

我有一个类,其中有一个公共方法和许多私有方法,它们的运行取决于传递给公共方法的参数,因此我的代码如下所示:

public class SomeComplexClass
{
    IRepository _repository;

    public SomeComplexClass()
       this(new Repository())
    {
    }

    public SomeComplexClass(IRepository repository)
    {
        _repository = repository;
    }


    public List<int> SomeComplexCalcualation(int option)
    {
        var list = new List<int>();

        if (option == 1)
            list = CalculateOptionOne();
        else if (option == 2)
            list = CalculateOptionTwo();
        else if (option == 3)
            list = CalculateOptionThree();
        else if (option == 4)
            list = CalculateOptionFour();
        else if (option == 5)
            list = CalculateOptionFive();

        return list;
    }

    private List<int> CalculateOptionOne()
    {
        // Some calculation
    }

    private List<int> CalculateOptionTwo()
    {
        // Some calculation
    }

    private List<int> CalculateOptionThree()
    {
        // Some calculation
    }

    private List<int> CalculateOptionFour()
    {
        // Some calculation
    }

    private List<int> CalculateOptionFive()
    {
        // Some calculation
    }
}
公共类SomeComplexClass
{
i存储库;
公共SomeComplexClass()
此(新存储库())
{
}
公共SomeComplexClass(IRepository存储库)
{
_存储库=存储库;
}
公共列表某些复杂参数(int选项)
{
var list=新列表();
如果(选项==1)
list=calculationOne();
否则如果(选项==2)
list=CalculateOptionTwo();
否则如果(选项==3)
列表=计算选项三();
否则如果(选项==4)
list=calculationfour();
否则如果(选项==5)
列表=CalculateOptionFive();
退货清单;
}
私有列表CalculationOne()
{
//一些计算
}
私有列表计算选项二()
{
//一些计算
}
私有列表计算选项三()
{
//一些计算
}
私有列表计算选项四()
{
//一些计算
}
私有列表计算选项五()
{
//一些计算
}
}
我已经想到了一些方法来测试这个类,但是所有这些方法都显得过于复杂,或者超出了我的想象。目前的选择是:

  • 将所有私有方法设置为internal并使用[assembly:InternalsVisibleTo()]

  • 将所有私有方法分离到一个单独的类中,并创建一个接口

  • 将所有方法都设为虚拟,并在我的测试中创建一个从该类继承的新类并重写这些方法

有没有比我列出的更好的方法来测试上面的类

如果你选择我列出的其中一个,你能解释一下原因吗


谢谢

那么OptionCalculator呢,原始类将工作分派给它?每一个都只有一个方法CalculateOption,当然它是公共的,因此易于测试。

OptionCalculator如何,原始类将工作分派给它?每个都只有一个方法CalculateOption,当然它是公共的,因此易于测试。

您不需要更改接口来测试这些方法。只需彻底测试公共接口,以确保所有私有方法都经过测试:

 void Test1() 
 {
      new SomeComplexClass(foo).SomeComplexCalcualation(1);
 } 

 void Test2() 
 {
      new SomeComplexClass(foo).SomeComplexCalcualation(2);
 } 
等等


您可以使用覆盖率工具(例如for.NET)来确保您想要测试的所有代码都已经过测试。

您不需要更改接口来测试这些方法。只需彻底测试公共接口,以确保所有私有方法都经过测试:

 void Test1() 
 {
      new SomeComplexClass(foo).SomeComplexCalcualation(1);
 } 

 void Test2() 
 {
      new SomeComplexClass(foo).SomeComplexCalcualation(2);
 } 
等等


您可以使用覆盖率工具(例如for.NET)来确保您想要测试的所有代码都已经过测试。

您应该只需要测试类/接口的公共方法


您只需要确保您有足够的单元测试用例来彻底测试这些公共方法的所有不同行为(这将适当地执行私有方法)。

您应该只需要测试类/接口的公共方法


您只需要确保您有足够的单元测试用例来彻底测试这些公共方法的所有不同行为(这将适当地执行私有方法)。

如果每个计算都那么复杂,那么每个计算真的是单个方法吗?如果这些计算共享代码,或者每个计算应该有多个方法,那么这就是您提到的接口/策略方法的一个参数,因此您可以测试这些步骤中的每一个

另一件需要考虑的事情是:彻底使用一个公共方法就是测试两件事 a) ComputeOptionN代码是否正常工作,以及 b) 选项检查是否正常工作


如果你真的在传递整数,这不是一个问题,但并不理想,特别是如果比较可能变得更复杂,或者它可能会改变

如果每种计算都那么复杂,那么每种计算真的是一种单一的方法吗?如果这些计算共享代码,或者每个计算应该有多个方法,那么这就是您提到的接口/策略方法的一个参数,因此您可以测试这些步骤中的每一个

另一件需要考虑的事情是:彻底使用一个公共方法就是测试两件事 a) ComputeOptionN代码是否正常工作,以及 b) 选项检查是否正常工作


如果你真的在传递整数,这不是一个问题,但并不理想,特别是如果比较可能变得更复杂,或者它可能会改变

卡尔是对的。这只不过是利用一种战略模式。将所有
计算器
存储在一个数组中,将数组注入
SomeComplexClass
。这将允许您单独对每个
计算器和
SomeComplexClass
进行单元测试。现在您可以执行以下操作:

public List<int> SomeComplexCalcualation(int option)
{
     return calculator.find(option);
}
public List somecomplexCalculation(int选项)
{
返回计算器。查找(选项);
}

非常容易模仿的
计算器

@Carl是对的。这只不过是利用一种战略模式。将所有
计算器
存储在一个数组中,将数组注入
SomeComplexClass
。这将允许您单独对每个
计算器和
SomeComplexClass
进行单元测试。现在您可以执行以下操作:

public List<int> SomeComplexCalcualation(int option)
{
     return calculator.find(option);
}
public List somecomplexCalculation(int选项)
{
返回计算器。查找(选项);
}

很容易模仿
计算器

这与您的问题无关,但是if/else构造让我觉得很奇怪。从设计的角度来看,您经常让该类的客户端调用多种类型的计算,还是只调用一种类型的计算