0

我对私有/受保护方法的测试有疑问。这是一个针对任何平台的单元测试的一般问题。但只是告诉你,我正在使用 phpunit 一个平台来对 php 进行单元测试。

我们应该测试私有/受保护的方法吗?在这个问题上接受的答案说我们通常不应该。从那个答案:

通常你只是不直接测试或模拟私有和受保护的方法。

您要测试的是您班级的公共 API。其他所有内容都是您的课程的实现细节,如果您更改它,则不会“破坏”您的测试。

但与此同时,许多问题中的其他答案提供了一种测试它们的方法(我认为这些答案意味着我们应该测试私有/受保护的方法,因为他们没有说我们应该或不应该测试它们)。

请给我解释理由。谢谢你。

4

3 回答 3

1

私有和受保护的方法是被测代码的实现细节,不应进行测试。你的测试告诉你你的代码应该做什么。测试私有和受保护的方法开始研究你的代码应该如何做它正在做的事情。

当您修改类以添加一些私有方法时,因为您发现一些代码重用并且您的测试失败,您知道这是因为您更改了某些功能并且需要修复该测试用例。如果您对私有方法和受保护方法进行了测试,并且您现在执行相同的操作,则需要查看测试是否由于正当原因而失败或来自重构。私有和受保护的方法通过类的公共接口的测试来覆盖,不需要它们自己的任何测试。

仅仅因为你“可以”做某事并不意味着你“应该”做某事。

当我在测试时,我认为被测系统是一个黑匣子。我给了它一些东西,我期望它会发生一些事情。我不在乎里面发生了什么。重要的是给出正确的输出。

复杂的内部函数是一种代码味道,也许我的班级做得太多,可能还有另一个班级隐藏在里面。

于 2013-07-30T14:00:17.117 回答
1

至于我 - 这取决于私人/受保护成员的复杂性。

例如,您有具有复杂计算或具有许多输入参数的算法的私有方法Func1 () 和Func2 ()。而且您有一个同时使用它们的公共 API。并且在某些数据上Func1 () 被破坏并返回不正确的数据,但在Func2 () 处理期间,该数据以某种方式转换为正确的结果(由于其他问题)。公共 API 方法的总结果将是正确的 - 这意味着您的Func1Func2不正确,但您的 API 以某种方式返回正确。你测试它,这很好。

现在您将代码提供给某人,他/她创建了 Func3() 和另一个使用Func1 () 和Func3 () 的公共 API 方法,并且对该 API 方法的单元测试失败。他/她将花费多少时间来找出原因在Func1中?当Func1被修复时,您将在Func2失败的情况下进行先前的测试......这完全不好。

因此,恕我直言,如果私有/受保护方法很复杂(大量代码或使用不明显的算法)且可重用,您应该测试它们。当然,您不需要为返回 a + b 或编写调试日志记录的私有方法创建 100 个单元测试。

希望我的回答对你有帮助!最好的问候,米哈伊尔。

于 2013-07-30T07:42:06.620 回答
0

我相信私有/受保护方法的测试取决于访问它们的位置/方式以及正在测试的内容。

如果我正在构建一个可重用的通用代码库,那么这些类中的私有和受保护方法将在库测试用例中进行测试。这些测试确保较低级别的代码可以正常工作,因此我可以对库有信心。

现在,当我编写访问该库的业务应用程序时,我不会为库对象编写测试,因为它们将在业务应用程序的测试中被模拟。库测试显示库工作,然后业务应用程序测试显示应用程序工作。

业务应用程序不会尝试测试库的私有/受保护方法,因为我认为这是一个我不知道的黑盒代码(类似于外部库或 Web 服务)。但是,我确实在业务应用程序的测试套件中测试了业务应用程序的私有方法和受保护方法,以确保方法/功能按应有的方式运行。

作为一个例子,假设以下业务类(非常简短地传达思想,而不是功能):

<?php
class ACCOUNTS
{
    protected GetEstimation()
    {
        ...
        return $CalculatedValue;
    }
}

class CUSTOMER_ACCOUNT extends ACCOUNTS
{
    protected GetEstimation()
    {
        $BaseEstimation = parent::GetEstimation();
        ...
        return $NewCalculatedValue
    }
}

class RESELLER_ACCOUNT extends ACCOUNTS
{
    protected GetEstimation()
    {
        ...
        return $ResellerCalculatedValue; // Note: No call to parent
    }
}
?>

在此示例中,将返回不同的值。使用了受保护的函数,因此它可以被覆盖,并且它不必依赖父类的功能。在此示例中,我确实想测试所有这些类在使用时返回正确的值。

基本 ACCOUNTS 类应在根据类值进行计算后返回 Estimation。通常,我只需设置值并测试返回:

<?php
class ACCOUNTSTest extends PHPUnit_Framework_TestCase
{
    protected $Accounts;

    protected function setUp()
    {
        $this->Accounts = new ACCOUNTS();
    }

    public function testEstimationHome()
    {
        $this->Accounts->InternalValue1 = 1;
        $this->Accounts->InternalValue2 = 10;
        $this->Accounts->InternalValue3 = 100;
        $this->assertEquals(523, $this->Accounts->GetEstimation(), 'Test Home Account with values 1, 10, 1000');
    }

    public function testEstimationHome2()
    {
        $this->Accounts->InternalValue1 = 5;
        $this->Accounts->InternalValue2 = 2;
        $this->Accounts->InternalValue3 = 10;
        $this->assertEquals(253, $this->Accounts->GetEstimation(), 'Test Home Account with values 5, 2, 10');
    }

    protected function tearDown()
    {
        unset($this->Accounts);
    }
}
?>

这些测试现在将确保 ACCOUNTS->GetEstimation() 正常工作。然后我会测试 CUSTOMER_ACCOUNT,并进行类似的测试以确保该类计算正确。

是的,如果基类发生变化,那么我可能需要更新 CUSTOMER_ACCOUNT 中的测试,因为 ACCOUNTS->GetEstimation() 发生了变化,但我还要额外检查基类是否仍然正确返回。

或者,我可以更改此结构并使用依赖注入来提供某些信息(帐户),以确保如果父估计始终为 523,则此类返回正确的值。如果 ACCOUNTS 更改了返回的估计值,并且对这个类无关紧要(也许这个类只是添加附加值),那么我隔离我的测试并且不需要担心。

希望此更新有助于说明我在说什么。

于 2013-07-30T12:48:44.687 回答