【问题标题】:How can I use TDD to build my little PHP framework?如何使用 TDD 构建我的小型 PHP 框架?
【发布时间】:2017-12-29 17:26:42
【问题描述】:

我正在努力养成测试我的代码的好习惯,我在这个主题上做了一些练习,但一切都很简单,当我试图将我学到的知识应用到实际应用程序时,我感到非常困惑.

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

我想使用 TDD 来开发这个框架,所以我开始测试我的 RouterAdapter 类,我完全迷失了。我可以测试一个简单的计算器类,但是如何测试一个严重依赖于一个我真的不知道它是如何工作的类的类?我知道我必须存储一个 RouteCollector 对象才能存储路由,我知道这个对象有一些方法,如 getData(),它返回一个数组数组,但是我如何测试这样的方法:

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

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

private $routeCollector;

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

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

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];
}

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

【问题讨论】:

    标签: php unit-testing phpunit tdd code-testing


    【解决方案1】:

    在这种情况下,您可以使用模拟框架。理想情况下,传递给构造函数的 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);
    }
    

    【讨论】:

    • 非常感谢,我使用了 phpunit 内置的 MockBuilder。我模拟了 routeCollector 对象并验证了 mock->addRoute 被调用了一次。所以我继续前进,写了一个 Router->get() 方法,我想测试 get() 是否会调用 Router->addRoute('GET', ... , ....);这次我对Router类做了同样的模拟,但是这次没有运气,报告说方法'addRoute'应该被调用一次,而是0。
    猜你喜欢
    • 1970-01-01
    • 2017-02-19
    • 2010-10-10
    • 2018-05-10
    • 1970-01-01
    • 2016-11-14
    • 1970-01-01
    • 1970-01-01
    • 2011-02-02
    相关资源
    最近更新 更多