从抽象类测试子类的抽象方法

时间:2016-05-06 14:56:30

标签: php unit-testing phpunit abstract-class

为了坚持使用here

的相同示例

我现在想测试子类中受保护方法的实现 因为我在抽象类的测试中将它们存在,所以实现本身并未经过测试 但是,受保护的方法没有正常测试,所以我想了解你如何测试它们的建议。

就像my other thread一样,我想解决这个而不重构我的代码

父级:

abstract class Order
{
    public function __construct( $orderId, User $user )
    {
        $this->id = $this->findOrderId( $user->getId(), $orderId );

        if ($this->id !== false) {
            $this->setOrderData();
        }
    }

    abstract protected function findOrderId( $userId, $orderIdToSearch );

    private function setOrderData()
    {
        ...
    }
}

要测试的儿童班:

public class OrderTypeA extends Order
{
    protected function findOrderId($userId, $orderId)
    {
        ...
    }
}

测试代码:

class OrderTypeATest extends PHPUnit_Framework_TestCase
{
    public function testFindOrderId() {
        ???
    }
}

3 个答案:

答案 0 :(得分:1)

您可以使用反射测试受保护/私有方法。阅读此tutorial。在其他解决方案中,你会发现直接的解决方案:

/**
 * Call protected/private method of a class.
 *
 * @param object &$object    Instantiated object that we will run method on.
 * @param string $methodName Method name to call
 * @param array  $parameters Array of parameters to pass into method.
 *
 * @return mixed Method return.
 */
 public function invokeMethod(&$object, $methodName, array $parameters = array())
 {
     $reflection = new \ReflectionClass(get_class($object));
     $method = $reflection->getMethod($methodName);
     $method->setAccessible(true);

     return $method->invokeArgs($object, $parameters);
 }

另外,关于你的上一个问题,你试图测试抽象类。 phpunit mocking的解决方案必须有效。但是如果使用PHP 7,则可以使用Anonymous classes来获得相同的结果:

abstract class Order
{
    protected $id;

    public function __construct($orderId, $userId)
    {
        $this->id = $this->findOrderId($userId, $orderId);

        if ($this->id !== false) {
            $this->setOrderData();
        }
    }

    abstract protected function findOrderId($userId, $orderIdToSearch);

    private function setOrderData()
    {
        echo 'setOrderData';
    }
}

$orderId = 1;
$userId = 1;

$order = new class($orderId, $userId) extends Order {
    protected function findOrderId($userId, $orderIdToSearch)
    {
        return 1;
    }
};

您将最终得到正在运行的 $ order 对象,该对象已准备好进行测试。最好将此代码放在测试用例的 setUp()方法中。

答案 1 :(得分:0)

如果您在订单找到正确时才获得有效的Generic.xaml。做一些像:

$this->id

$order = new OrderTypeA($orderId, $user); $this->assertNotEquals(false,$order->id); 等于$orderId

$this->id

但是这里没有足够的代码/逻辑告诉你更多;)

答案 2 :(得分:0)

你的抽象对我没有意义。

我知道你有一个代表订单的对象。您通过提供用户和订单ID来实例化它。但是,有多种类型的订单,这些类型的订单之间的区别在于您在数据库存储中搜索它们的方式?这听起来不对。

您的代码确实讲述了一个奇怪的故事。您有此订单ID,您要做的第一件事就是搜索订单ID。我只是认为你已经拥有订单ID,所以不需要再次搜索它。或者该方法名称错误,而不是findOrderId(),而应该将其称为findOrderById() - 或findUserOrderById()

此外,您在构造函数中工作。不应该在那里搜索东西。

您的测试问题来自于您决定将不同的搜索策略实现为抽象方法。您必须测试受保护的抽象方法,这并不容易。它使得对主抽象订单类进行属性测试也很困难,因为你必须提供一个实现 - 而这个实现听起来像是隐藏了一个数据库访问层,所以实际代码中可能会出现很多问题。

我建议不要让订单自行搜索。搜索订单应在订单对象之外完成。这样,您可能会将搜索作为公共方法实现,可以进行常规测试。然后,搜索订单的代码将决定您是否已成功找到OrderTypeA,或者可能缺少MissingOrderTypeA,这两个版本都会延长订单类。订单对象应该包含订单数据,而不是搜索逻辑以在数据库中找到它们。

提示:如果您在测试代码时遇到问题,那么您的代码尝试以错误的方式执行操作的可能性为99.9%。这并不是说事情不能以这种方式完成,而是说你将要努力测试代码,这也难以维护,并且寻找替代策略来实现解决方案是个好主意。优雅的代码总是很容易测试,因为所有必要的方法在相关的类中是公开的,因此可以按预期一起工作。