3

我正在尝试遵循/学习 TDD。我正在使用 PHPUnit。目前,我正在编写小型、非常简单的类/项目,只是为了将其全部纳入其中。

目前我正在编写游戏卡设置。

所以开始它,我写了这个测试,因为我会避免在我的牌花色中重复,我决定测试应该是一个抽象类,花色可以扩展。

希望当你读完这篇文章时,课程的模式是显而易见的。

BaseSuitTest.php

use Cards\French\Suits\BaseSuit as Card;

class BaseSuitTest extends PHPUnit_Framework_TestCase 
{

    protected $Card;

    public function setUp()
    {
        $this->Card = $this->getMockForAbstractClass('Cards\French\Suits\BaseSuit', [10]);
    }


    /**
    *   @expectedException InvalidArgumentException
    */
    public function testConstructThrowsExceptionIfFirstArgLessThan1()
    {
        $Card = $this->getMockForAbstractClass('Cards\French\Suits\BaseSuit', [0]);
    }


    /**
    *   @expectedException InvalidArgumentException
    */
    public function testConstructThrowsExceptionIfFirstArgHigherThan13()
    {
        $Card = $this->getMockForAbstractClass('Cards\French\Suits\BaseSuit', [14]);
    }


    public function testGetSuitReturnsClassName() 
    {
        $suit = $this->Card->getSuit();
        $this->assertSame( get_class($this->Card), $suit);
    }


    public function testGetValueReturnsValue()
    {
        $value = $this->Card->getValue();
        $this->assertSame(10, $value);
    }
}

之后我编写了以下文件,使所有测试通过。在这一点上,我也在考虑是否应该为 BaseSuit 实现 CardInterface(我应该编写一个测试来检查实现还是这不是测试的一部分)。我决定不去。

BaseSuit.php

namespace Cards\French\Suits;

abstract class BaseSuit 
{

    const MIN_VALUE = 1;
    const MAX_VALUE = 13;


    protected $value;


    public function __construct($value)
    {
        if( $this->isWithinValueRange($value) === false)
            throw new \InvalidArgumentException('The value must be higer than 0 and less than 14 (1-13) Given ' . $value);
        $this->value = $value;
    }


    protected function isWithinValueRange($value)
    {
        if($value < self::MIN_VALUE OR $value > self::MAX_VALUE)
            return false;
        return true;
    }


    public function getSuit()
    {
        return get_class($this);
    }


    public function getValue()
    {
         return $this->value;
    }
}

最后,我开始对从基类扩展的实际类/套装进行测试。

该测试与 BaseSuit 中的测试非常相似,这已经让我在脑海中感到烦恼。

HeartTest.php

use Cards\French\Suits\Heart as Heart;

class HeartTest extends PHPUnit_Framework_TestCase 
{

    protected $Heart;


    public function setUp()
    {
        $this->Heart = new Heart(10);
    }


    /**
    *   @expectedException InvalidArgumentException
    */
    public function testConstructThrowsExceptionIfFirstArgLessThan1()
    {
        $Heart = new Heart(0);
    }


    /**
    *   @expectedException InvalidArgumentException
    */
    public function testConstructThrowsExceptionIfFirstArgHigherThan13()
    {
        $Heart = new Heart(14);
    }


    public function testGetSuitReturnsClassName() 
    {
        $suit = $this->Heart->getSuit();
        $this->assertSame( get_class($this->Heart), $suit);
    }


    public function testGetValueReturnsValue()
    {
        $value = $this->Heart->getValue();
        $this->assertSame(10, $value);
    }
}

我再次编写代码以使测试通过。

心脏.php

namespace Cards\French\Suits;

class Heart extends BaseSuit {}

所有的测试都通过了,但一切都不是很酷。我想避免重复从 BaseSuit 延伸的套装(如 Heart)的测试。

我最初的想法是将 BaseSuit 中的方法重命名为 protected ,然后从这个测试中扩展。这是重命名的 BaseSuit 测试之一的示例。

BaseSuit.php 中的重构方法

/**
*   @expectedException InvalidArgumentException
*/
protected function constructThrowsExceptionIfFirstArgLessThan1($class)
{
    // .. i have not written this code, it just a figment of my imagination.
}

这是我分裂的地方。我不确定如何继续。

将 BaseSuitTest 中的测试转移到受保护的方法意味着我要么放弃当前的公共测试方法。这可以修复我制作调用受保护方法的新测试方法。

代码示例、重构建议、模式建议以及阅读本文时脑海中浮现的任何建设性内容,我都会非常感激。

有人建议我为在同一方法上运行的测试使用提供程序。聪明,我会在将来这样做,但我无法看到这如何解决我的情况。只是当它是抽象的 BaseSuit 时,我想在测试中使用 getMockForAbstract,而当它是西装时,我只是应该更像 HeartTest。我需要帮助了解如何以正确的方式实现这一点。

4

1 回答 1

2

我认为您错过了 TDD 的主要目标。与其询问如何重构测试以避免代码和工作重复,不如使用测试来告知非测试代码的设计。为什么是Heart自己的类?它改变或增加了哪些行为BaseSuit将不同于SpadeDiamondClub

您已经将卡片的序数值变成了数据属性;你应该对西装做同样的事情。而不是每张牌都是具有价值的花色,而应该具有花色和价值。这使您可以拥有一个单独的具体Card类,而无需基础或抽象版本。创建一个类,该类使用常量和排序所需的任何方法(尽管这些可能属于一个类)Suits来定义不同的花色Rules

如果您仍然发现需要对套装进行更复杂的建模,我会坚持使用具有、、等作为数据属性的单个泛型Suit类。namesymbolorder

于 2013-10-05T18:10:23.040 回答