dancras/doubles

测试双倍框架

1.0.0 2012-10-16 13:54 UTC

This package is not auto-updated.

Last update: 2024-09-14 13:18:05 UTC


README

Doubles是一个php测试双倍库,旨在提供简单、逻辑化的API以及编写单元测试时的语法糖。它特别适合单元测试中的arrange-act-assert模式,尽管它并不刻意强加任何风格。它是为phpunit设计的,但没有依赖项,也可以与其他测试框架一起使用。它目前处于早期开发阶段,任何反馈都非常欢迎。

需要PHP 5.3以上。

不需要对单元测试框架进行任何更改即可集成。

版权(c)2012 Daniel Howlett

双重许可下MIT和GPL许可证。

反馈可以在http://www.dancras.co.uk留下

Build Status

使用composer安装

要从packagist安装,请将以下内容添加到您的composer.json文件中

{
    "minimum-stability": "dev",
    "require": {
        "dancras/doubles": "*"
    }
}

别忘了将vendor/autoload.php包含在您的代码中。

已知问题

  • 如果您的类有与所选测试双倍相匹配的方法,目前没有方法可以访问测试双倍方法。

  • 由库触发的失败将以E代码而不是F代码在phpunit中失败测试。

  • 目前不支持PHP 5.4功能,例如“callable”类型提示。

参考

添加以下内容

use Doubles\Doubles;

完整测试双倍

用主题的所有方法创建测试双倍

$double = Doubles::fromClass('\MyClass');

$double = Doubles::fromInterface('\MyInterface');

如果主题不存在,Doubles将创建主题。

部分测试双倍

部分测试双倍的方法在它们被存根、模拟或拦截之前不受影响。它们从一个主题实例中创建。

$subject = new \Doubles\Test\Dummy;

$double = Doubles::partial($subject);

可能需要跳过主题的构造函数

$subject = Doubles::noConstruct('\Doubles\Test\Dummy');

间谍

提供对测试双倍交互历史的访问,包括部分测试双倍未受影响的方法。一个图形服务测试双倍可能执行以下操作

$double->plot(0, 5);

$double->plot(2, 6);

$double->setLineColour('red');

$double->render();

代码运行后,我们可以调查测试双倍

$double->spy('plot')->args(0); // array(0, 5)

$double->spy('plot')->args(); // array(array(0, 5), array(2, 6))

$double->spy('plot')->findArgs(2, 6); // 1

$double->spy('plot')->arg(1, 0); // 2

$double->spy('plot')->callCount(); // 2

$double->callCount(); // 4

一次调用

当方法期望一次调用时,我们可以使用一次调用变体来避免多余的断言。注意,在一次性变体中使用时,可以省略调用索引。

$double->spy('setLineColour')->oneCallArgs(); // array('red')

$double->spy('setLineColour')->oneCallArgs(0); // 'red'

如果方法没有收到正好一次调用,一次性方法将抛出异常。

$double->spy('plot')->oneCallArgs(); // throws \Doubles\Core\FailureException

$double->spy('foo')->oneCallArgs(); // throws \Doubles\Core\FailureException

调用顺序

从一开始。可以用来断言方法以预期的顺序被调用。

$double->spy('plot')->callOrder(0); // 1

$double->spy('plot')->callOrder(1); // 2

一次性变体也适用于调用顺序

$double->spy('render')->oneCallOrder(0); // 4

以下代码断言render最后只调用一次

$this->assertSame(
    $double->callCount(),
    $double->spy('render')->oneCallOrder(0)
); // pass

共享调用顺序

有时需要比较实例之间的调用顺序。Doubles\Spy\CallCounter::shareNew()方法分发一个共享调用计数器。假设本例中的所有对象都已作为测试双倍创建

use Doubles\Spy\CallCounter;

CallCounter::shareNew($pizza, $waiter, $customer);

以下操作被错误地执行在我们的对象上。我们的不耐烦的客户似乎在帮助他自己

$pizza->cook();

$customer->eat($pizza);

$waiter->take($pizza);

使用共享调用顺序,我们可以在测试中捕获这个错误。披萨必须在服务员取之前烹饪

$this->assertGreaterThan(
    $pizza->spy('cook')->oneSharedCallOrder(),
    $waiter->spy('take')->sharedCallOrder(0)
); // pass

服务员必须在接受披萨之前取披萨

$this->assertGreaterThan(
    $waiter->spy('take')->sharedCallOrder(0),
    $customer->spy('eat')->sharedCallOrder(0)
); // fail

再次注意一次性变体,确保我们的披萨不会烧焦。

存根

$double->stub('foo', 'bar');

$double->foo(); // 'bar'

我们可以同时存根多个方法,例如,存根流畅接口

$double->stub('setX', 'setY', $double);

$double->setX(); // $double

$double->setY(); // $double

存根也可以抛出异常

$double->stub('boom', new EndOfTheWorldException);

$double->boom(); // throws EndOfTheWorldException

要实际返回一个异常,您需要使用模拟。

模拟

模拟是测试方法的最高效方式,但可能难以理解。

$myObject->mock('give', function ($methodName, $arguments) use (&$m, &$a) {
    $m = $methodName; // 'give'
    $a = $arguments; // array(1, 2, 3)
    return 'result';
});

$myObject->give(1, 2, 3); // 'result'

不建议在模拟回调中执行断言。如果您的代码未能调用该方法,则不会执行任何断言,测试可能会通过。

如果您在模拟中执行断言,可能想要使用间谍对象。或者,通过引用使用变量将允许您在闭包外部执行断言。

拦截器

拦截是一种改进的模拟形式,可用于部分对象,提供部分主题的实例给回调。

$myObject->intercept('foo', function ($methodName, $arguments, $instance) use (&$m, &$a) {
    $m = $methodName; // 'foo'
    $a = $arguments; // array(1, 2, 3)
    return $instances->foo($a);
});

$myObject->give(1, 2, 3); // 'result'

期望

当您模拟或存根一个方法时,它就变成了期望。默认情况下,对未期望的方法的调用没有影响。

$myObject->unknown(); // null

$myObject->setUnexpectedMethodCallback(function ($methodName, $arguments) {
    throw new Exception;
});

$myObject->unknown(); // throws Exception

快速原型设计

默认情况下,当使用测试替身(test double)对一个已定义类型进行测试时,如果调用了一个原始类或测试替身API上不存在的方法,则会抛出异常。

如果该类型不存在,则被认为是快速原型设计(因为必须事先定义所有类签名变得繁琐)。在此模式下,可以使用任何方法。当您定义类或接口时,测试将失败,直到您完成其签名。

我发现这种行为非常有效,但如果这不是期望的功能,则可以强制启用快速原型设计模式,这意味着可以在类或接口签名之外自由使用方法。

\Doubles\Core\TestDouble::$isRapidPrototypingEnabled = true;