lucatume/tdd-helpers

该软件包已被弃用,且不再维护。未建议替代软件包。

PHP 5.2 的 TDD 辅助工具

2.4.5 2014-10-01 12:46 UTC

This package is not auto-updated.

Last update: 2015-12-05 10:25:03 UTC


README

该软件包提供两个用于全局定义函数和变量的适配器以及一个静态模拟类。
这些适配器满足了我将 TDD 技术应用于 WordPress 插件和主题开发的需求。

适配器

函数适配器

将全局定义的函数的调用包装在方法调用中。如果 some_function 是在全局作用域中定义的函数,则可以使用适配器调用它,例如:

$adapter = new tad_FunctionsAdapter();
$var = $adapter->some_function();

该适配器使用接口以实现更灵活的测试模拟

$mockF = $this->getMock('tad_FunctionsAdapterInterface');

全局变量适配器

允许通过对象方法方式访问超级全局变量。
访问 $GLOBALS['foo'] 的示例

$g = new tad_GlobalsAdapter();
$foo = $g->globals('foo');

要获取超级全局数组,请不带参数调用函数,例如,要获取 $_SERVER 数组

$g = new tad_GlobalsAdapter();
$g->server();

静态模拟器

一个测试辅助工具,可以以某种方式和限制来模拟静态方法调用。
要测试的类应允许进行类名注入,例如:

public function __construct($var1, $var2, $util = 'StaticClass')
{
    $this->util = $util;

    $var = $this->util::doSomething();
}

然后在测试文件中

class StaticClass extends tad_StaticMocker
{}


class ClassNameTest extends \PHPUnit_Framework_TestCase
{
    public function test_construct_calls_static_class_doSomething()
    {
        // Create a stub for the SomeClass class.
        $stub = $this->getMock('SomeClass');

        // Configure the stub.
        $stub->expects($this->any())
            ->method('doSomething')
            ->will($this->returnValue('foo'));

        StaticClass::_setListener($stub);

        $sut = new ClassName('some', 'var', 'StaticClass');
    }
}

可测试的对象

abstracttad_TestableObject 旨在用作任何打算使用 TDD 技术开发的类的父类,以便更快地对一个或多个方法依赖项进行模拟。测试类似于以下类的类:

class ClassOne{

    protected $d;

    public function __construct(D $d){
        $this->d = $d;
    }

    public function methodOne(A $a, BInterface $b, CInterface $c){
        $a->method();
        $b->method();
        $c->method();
        $this->d->method();
    }
}

将需要模拟 AD 类的实例以及 BInterfaceCInterface 接口的实例

// file ClassOneTest.php

public function test_methodOne_will_call_methods(){
    $mockA = $this->getMock('A', array('method'));
    $mockBInterface = $this->getMock('BInterface', array('method'));
    $mockCInterface = $this->getMock('CInterface', array('method'));
    $mockD = $this->getMock('D', array('method'));

    $mockA->expects($this->once())->method('method');
    $mockBInterface->expects($this->once())->method('method');
    $mockCInterface->expects($this->once())->method('method');
    $mockD->expects($this->once())->method('method');

    $sut = new ClassOne($mockD);

    $sut->methodOne();
}

tad_TestableObject 类中定义的 getMocks 方法允许在存在 DocBlock 注释的情况下,将待测试的类重写为添加 DocBlock 注释的类

class ClassOne extends tad_TestableObject {

    protected $d;

    /**
     * @depends D
     */
    public function __construct(D $d){
        $this->d = $d;
    }

    /**
     * @depends A, BInterface, CInterface
     */
    public function methodOne(A $a, BInterface $b, CInterface $c){
        $a->method();
        $b->method();
        $c->method();
        $this->d->method();
    }
}

并将测试方法重写为

// file ClassOneTest.php

public function test_methodOne_will_call_methods(){

    $mockedDependencies = ClassOne::getMocksFor(array('__construct', 'methodOne'))     

    $mockDependencies->A->expects($this->once())->method('method');
    $mockDependencies->BInterface->expects($this->once())->method('method');
    $mockDependencies->CInterface->expects($this->once())->method('method');
    $mockDependencies->D->expects($this->once())->method('method');

    $sut = new ClassOne($mockedDependencies->$mockD);

    $sut->methodOne();
}

或者可以使用 getMocksArrayFor 方法以数组形式检索模拟,例如:

// file ClassOneTest.php

public function test_methodOne_will_call_methods(){

    extract(ClassOne::getMocksArrayFor(array('__construct', 'methodOne')));

    $A->expects($this->once())->method('method');
    $BInterface->expects($this->once())->method('method');
    $CInterface->expects($this->once())->method('method');
    $D->expects($this->once())->method('method');

    $sut = new ClassOne($D);

    $sut->methodOne();
}

tad_DependencyMocker

使用 tad_DependencyMocker 类也可以对未扩展 tad_TestableObject 类的类进行上述所有模拟。如果一个类的 DocBlocks 设置如下,但未扩展 tad_TestableObject

class ClassOne {

    protected $d;

    /**
     * @depends D
     */
    public function __construct(D $d){
        $this->d = $d;
    }

    /**
     * @depends A, BInterface, CInterface
     */
    public function methodOne(A $a, BInterface $b, CInterface $c){
        $a->method();
        $b->method();
        $c->method();
        $this->d->method();
    }
}

则其方法依赖项仍然可以进行模拟,例如:

// file ClassOneTest.php

public function test_methodOne_will_call_methods(){

    $extraMethods = array(
            'BInterface' => array('fooMethod', 'bazMethod'),
            'CInterface' => array('barMethod')
        );

    extract(tad_DependencyMocker::on('ClassOne')
        ->forMethods(array('__construct', 'methodOne'))
        ->setExtraMethods($extraMethods)
        ->getMocksArray());

    $A->expects($this->once())->method('method');
    $BInterface->expects($this->once())->method('method');
    $BInterface->expects($this->once())->method('fooMethod');
    $BInterface->expects($this->once())->method('bazMethod');
    $CInterface->expects($this->once())->method('method');
    $CInterface->expects($this->once())->method('barMethod');
    $D->expects($this->once())->method('method');

    $sut = new ClassOne($D);

    $sut->methodOne();
}

方法

该类定义以下方法

  • __construct($className, $methodNameOrArray, $extraMethods, $notation) - 静态,基于指定的类返回类的实例;除了 $className 之外,所有其他参数都可以稍后设置。
  • on($className, $methodNameOrArray, $extraMethods, $notation) - 静态,基于指定的类返回类的实例;除了 $className 之外,所有其他参数都可以稍后设置。
  • forMethods($methodNameOrArray) - 设置类要模拟的方法或方法
  • setNotation($notation) - 设置用于从文档块中读取方法元信息的标记,默认标记为 "depends"。
  • setExtraMethods($extraMethods) - 设置一个数组,包含除了类已定义之外要存根的类/方法;在上面的示例中,BInterface 获取两个额外的存根方法,fooMethodbazMethod 它将不会实现。
  • getMocks() - 获取一个定义每个依赖项作为属性的 stdClass 对象,该属性可通过属性 "->" 标记访问。
  • getMocksArray() - 获取一个包含每个模拟依赖项的键/值对的数组。