如何在不重构单独类的情况下对私有代码进行单元测试?

时间:2010-09-29 19:05:21

标签: unit-testing language-agnostic oop

假设我有一个私人例程执行一些计算:

private function TCar.Speed: float
{
   Result = m_furlogs * 23;
}

但是现在我想开始更彻底地测试这个计算,所以我将它重构为一个单独的函数:

public function TCar.Speed: float
{
   Result = CalculateSpeed(m_furlogs);
}

private function TCar.CalculateSpeed(single furlogs): float 
{
   Result = furlogs * 23;
}

现在我可以在CalculateSpeed上执行各种测试:

Check( CalculateSpeed(0)  =  0);
Check( CalculateSpeed(1)  = 23);
Check( CalculateSpeed(2)  = 46);
Check( CalculateSpeed(88) = -1);

除非我无法执行这些测试,因为CalculateSpeedTCar是私有的。单元测试的一个抽象的局面是你永远不会测试私有代码 - 只有公共接口。实际上,* x * Unit通常不能构造为能够访问被测试的单独类的私有方法。

问题是该类的其余部分都没有设置为处理单元测试。这是第一个将进行任何类型测试的例程。并且很难为宿主类配置一组初始条件,这些条件允许我使用我想要的每组输入来测试CalculateSpeed

我能看到的唯一选择是将这个私有计算移到它自己的TCarCalculateSpeed类中:

public class TCarCalculateSpeed
{  
   public function CalculateSpeed(float furlogs)
   {
      Result = furlogs * 23;
   }
}

一个专门用于公开一种方法的全班,它应该是私有的,这样我才能测试它?

类爆炸。

另外它是私人的。如果我希望它是公开的,我宁愿将其推广到公共可见性 - 至少这样我保存了一个单独的类。

我想添加一些单元测试;但随着代码的变化,它只能以小块形式完成。我无法完全重新设计功能已有12年历史的软件,可能会破坏所有内容,因为我想测试一个内部计算。

我当前最好的想法是在我的Test课程中添加Car方法,然后调用它:

TCar Car = new TCar();
Car.RunTests;

public procedure TCar.RunTests
{
   Check( CalculateSpeed(0)  =  0);
   Check( CalculateSpeed(1)  = 23);
   Check( CalculateSpeed(2)  = 46);
   Check( CalculateSpeed(88) = -1);
}

但是现在我必须弄清楚如何让TCar.RunTests被外部TestRunner触发,而外部TestCase仅设计为使用{{1}}类。

注意:我已尝试过将该语法与一堆语言混合起来。换句话说:语言不可知。

6 个答案:

答案 0 :(得分:3)

如果一个方法很复杂(而且有风险)足以自行测试,那么考虑到现有类的特性,值得为它创建一个类或者使它成为现有类的公共成员 - 哪个更合适。

答案 1 :(得分:3)

这实际上与语言无关,因为保护机制和绕过它们的策略在语言上的差异很大。

但大多数语言确实以某种形式提供旁路,并且正如其他人所指出的那样,有时私人和公共之间的保护可以使测试变得更容易。

例如,在Java中,如果你真的需要,反射可以用于私有东西,并且可以保护事物或者包装私有,这样你就不需要反射。

一般来说,如果某些事情足够复杂,需要进行测试,则不应将其作为私有方法或类别隐藏在其他内容中。它正在做一些保证自己班级的事情。

不要担心课程的数量,而是担心课程的大小和复杂性。许多遵守Single Responsibility Principle的小班级比内部执行复杂事务的少数班级要好。

答案 2 :(得分:1)

您是否可以使用m_furlogs的不同初始值创建TCar类的多个实例?你有速度的吸气剂吗?如果是这样,你可以对此进行验证。

如果它只在内部使用,而你真的想测试它,你可以创建一个实用程序类来保存简单计算的逻辑。我知道这是重构,但这不是你可能想象的阶级爆炸。

答案 3 :(得分:1)

也许您可以创建一个源自您所测试的类的测试类?

以下是另一个SO question

的示例

答案 4 :(得分:1)

在某些语言中,私人和公共之间存在中间立场。您可以将逻辑上的私有方法暴露给单元测试,而不会将其暴露给世界。

在方法文档中,您可以记录该方法是私有的,但可以进行单元测试。

例如,在Java中,您可以创建私有方法package protected,并将单元测试放在同一个包中。在C#中,如果我没记错的话,你可以把它变成内部的。在C ++中,单元测试可能是朋友。

答案 5 :(得分:1)

如果您的语言支持编译器定义,您可以使用它们。

(Delphi中的示例代码)

在单元测试项目中,使用项目选项或在项目中每个单元中包含的包含文件中设置编译器条件定义。

{$DEFINE UNIT_TESTS}

在您的班级代码中,检查条件定义并相应地在public或protected和private之间切换:

{$IFDEF UNIT_TESTS}
  public // or protected 
{$ELSE}
  private
{$ENDIF} 
    function CalculateSpeed: float;

这意味着您的单元测试将具有您的方法所需的访问权限,而在生产代码中,它仍然是私有的。