如何使用 TDD 构建我的 PHP 小框架?

How can I use TDD to build my little PHP framework?

我正在努力养成测试我的代码的好习惯,我做了一些关于这个主题的练习,但一切都很简单,当我试图将我学到的东西应用到实际应用程序中时,我是非常困惑。

我正在编写一个 PHP 框架,使用 Auryn、Zend-Diactoros 和 Fast-Route 等软件包。我想深入研究接口,以便将来能够轻松地交换依赖关系,所以我想编写一个具有 addRoute() 和 match() 方法的 RouterInterface,然后我只需要做的就是为选择的包编写适配器,使其适应我的界面。

我想使用 TDD 来开发这个框架,所以我开始测试我的 RouterAdapter class,但我完全迷失了。我可以测试一个简单的计算器 class,但是如何测试一个 class,它在很大程度上取决于我真的不知道它是如何工作的 class?我知道我必须存储一个 RouteCollector 对象才能存储路由,我知道这个对象有一些方法,比如 getData() returns 一个数组数组,但是我如何测试这样的方法:

public function addRoute($methods, string $pattern, $handler)
{
    $this->routeCollector->addRoute($methods, $pattern, $handler);
}

class 的构造函数可能是这样的

private $routeCollector;

public function __construct(RouteCollector $rc)
{
    $this->routeCollector = $rc;
}

我的测试应该检查是否在 routeCollector 对象中添加了一条路由,但这需要 运行 检查我的 class 中的私有 属性。也许比我想象的更容易,但现在我真的没有有效的解决方案,除了编写实用程序方法来获取存储在对象中的数据,像这样:

public function getRoutes()
{
    /* 
        I want just the array in the first position because that array
        is the array where are stored not-named routes.
    */
    return $this->routeCollector->getData()[0];
}

可能是我想多了,这种编码方式对我来说真的很新鲜

在这种情况下,您可以使用模拟框架。您传递给构造函数的 RouteCollector 类型理想情况下应该是一个接口。

然后在您的单元测试中,您在该接口后面设置了一个模拟实现。或者,如果您出于某种原因无法传递接口,那么您可以创建一个模拟,它是 RouteCollector 的子类。使用典型的模拟框架,可以设置此模拟对象以验证是否使用特定参数调用了特定方法。在您的示例中,将设置的方法是 addRoute.

如果您没有模拟框架,则必须自己创建 RouteCollector 的模拟实现。然后你必须在你的模拟实现中实现一些机制来检查是否调用了某些方法(例如通过设置某种标志)然后你可以在你的单元测试中验证。

你不应该做的是在你的 类 中公开私有字段只是因为单元测试需要它们。

让我们以 Phake 模拟框架 (https://github.com/mlively/Phake) 为例。

我从来没有用过这个,所以我在快速阅读他们的文档 (http://phake.readthedocs.io/en/latest/method-verification.html) 的基础上写这篇文章。这是一个示例测试用例。

public function testAddRoute()
{
    // You want to see whether RouteCollector->addRoute is being called properly
    // For this you have to create a RouteCollector mock and let it verify the call
    $routeCollectorMock = Phake::mock('RouteCollector');

    // Setup your test
    $testCandidate = new YourClass($routeCollectorMock);

    // Call the method you want to test
    $testCandidate->addRoute($someMethods, $somePattern, $someHandler);

    // Verify that RouteCollector->addRoute was called with your passed parameters
    Phake::verify($routeCollectorMock)->addRoute($someMethods, $somePattern, $someHandler);
}