duanhui1869 2017-07-24 08:08
浏览 27
已采纳

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

I am trying to get into the good habit of testing my code, I did some exercise on the subject, but everything was so simple that, when I tried to apply what I learned on a real-world application I was extremely confused.

I'm writing a little PHP framework, using packages like Auryn, Zend-Diactoros and Fast-Route. I wanted to go heavy into interfaces to be able in future to swap dependencies with ease, so I wanted to write, for example, a RouterInterface that has a addRoute() and a match() methods, then what I just need to do is to write an adapter for the choosen package, to adapt it to my interface.

I wanted to use TDD to develop this framework, so I started with tests for my RouterAdapter class and I was totally lost. I can test a simple calculator class but how to test a class that depends heavily on a class that I don't really know how it works? I know that I have to store a RouteCollector object in order to store routes, I know that this object has some methods like getData() that returns an array of arrays, but how do I test a method like this one:

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

the class's constructor may be something like that

private $routeCollector;

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

My test should check that in the routeCollector Object a route was added, but this requires to run checks on a private property inside my class. Maybe is easier than I thought, but right now I don't really have a valid solution to this, other than write utility methods to get the data stored in the object, something like this:

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

Maybe I'm overthinking too much, this way of coding is really new for me

  • 写回答

1条回答 默认 最新

  • dougu1985 2017-07-24 08:31
    关注

    You can use a mocking framework in this case. The RouteCollector type which you pass into your constructor should ideally be an interface.

    Then in your unit test you setup a mock implementation behind that interface. Or if you for some reason cannot pass an interface, then you can create a mock which is a subclass of RouteCollector. With typical mocking frameworks this mock object then can be setup to verify whether a certain method was called with certain parameters. In your example the method which would be setup is addRoute.

    If you do not have a mocking framework then you have to create a mock implementation of RouteCollector on your own. You then have to implement some mechanism in your mock implementation to check whether certain methods were called (for example by setting some kind of flags) which you can then verify in your unit test.

    What you should not do is exposing private fields in your classes just because a unit test needs them.

    Let's use the Phake mocking framework (https://github.com/mlively/Phake) as an example.

    I have never used this so I am writing this based on a quick reading of their documentation (http://phake.readthedocs.io/en/latest/method-verification.html). Here is a sample testcase.

    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);
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 程序不包含适用于入口点的静态Main方法
  • ¥15 素材场景中光线烘焙后灯光失效
  • ¥15 请教一下各位,为什么我这个没有实现模拟点击
  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记