【问题标题】:Custom PHPUnit Constraint stopped working自定义 PHPUnit 约束停止工作
【发布时间】:2012-03-16 10:41:56
【问题描述】:

我为自己创建了一个自定义 PHPUnit 约束并将其连接到一个不错的 assert... 函数。

我将它放入我的基地TestCase,它是assertLastError 函数:

/**
 * abstract, base test-case class.
 */
abstract class TestCase extends \PHPUnit_Framework_TestCase
{
    /**
     * assert lint of a php file
     */
    protected function assertLint($filename, $message = '') {
        self::assertThat($filename, new ConstraintLint, $message);
    }
    /**
     * assert the last error
     */
    protected function assertLastError($error, $file, $message = '') {
        self::assertThat($file, new ConstraintLastError($error), $message);
    }
    /**
     * assert XML DTD validity
     */
    protected function assertXmlStringValidatesDtdUri($xml, $dtd, $message = '') {
        self::assertThat($dtd, new ConstraintXmlStringValidatesDtdUri($xml), $message);
    }

    ...

到目前为止,我已经调试了约束,我看到 evaluate 方法被调用并返回 FALSE,但是测试运行程序没有报告我失败。

约束:

/**
 * ConstraintLastError
 *
 * For asserting the last error message in a file.
 *
 * To test trigger_error().
 *
 * Example:
 *
 *   $this->assertLastError('Error Message', 'basenameOfFile.php');
 *
 */
class ConstraintLastError extends \PHPUnit_Framework_Constraint {
    private $file;
    private $error;
    public function __construct($error) {
        $this->error = $error;
    }
    /**
     * Evaluates the constraint for parameter $file. Returns TRUE if the
     * constraint is met, FALSE otherwise.
     *
     * @param string $file Value or object to evaluate.
     * @return bool
     */
    public function evaluate($file)
    {
        $this->file = $file;
        $error = $this->error;
        $lastError = error_get_last();
        if (NULL === $lastError)
            return false;

        $last_message = $lastError['message'];
        $last_file = $lastError['file'];


        $result = ($error == $last_message && basename($file) == basename($last_file));

        var_dump($result, $error, $last_message, $file, $last_file);

        return $result;
    }

    /**
     * @param mixed   $other
     * @param string  $description
     * @param boolean $not
     */
    protected function customFailureDescription($other, $description, $not)
    {
        return sprintf('Failed asserting that the last error %s', basename($other), $not ? '' : 'no ', implode("\n  - ", $this->lines));
    }


    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString()
    {
        return sprintf('was %s in file %s.', $this->error, $this->file);
    }
}

我不知道为什么它停止工作,测试用例刚刚完成,我可以在输出中看到错误消息,包括。 stacktrace(xdebug 已打开),var_dump 告诉我结果是FALSE。这是测试:

public function testGetType()
{
    ...

    $fragment->setParsed(array());
    \PHPUnit_Framework_Error_Warning::$enabled = FALSE;
    $actual = $fragment->getType();
    \PHPUnit_Framework_Error_Warning::$enabled = TRUE;
    $this->assertLastError('Invalid State Error FAIL', 'Fragment.php');
}

这是我刚写的一个新测试,我在其他地方也有同样的断言,它们也不再起作用了。

PHPUnit 3.6.7

【问题讨论】:

  • 您是否在代码中设置了自定义错误处理程序 (set_error_handler)?工作时是否启用了 xdebug?
  • 没有自定义错误处理程序(在我的代码中)。我不记得在工作时是否启用了 xdebug。我手动停用了早期 PHPUnit 版本的 xdebug 停用功能,默认情况下我的 dev xdebug 处于打开状态。所以很可能xdebug在它工作的时候被启用了,但我不记得了。不知何故,我有一种感觉,我正在为不测试自己的约束条件付出代价。
  • 您确定错误是首先触发的吗?如果不禁用 PHPUnit 的警告到异常转换会发生什么?
  • 如果我启用它,PHPUnit 会报告错误。失败!测试:4,断言:5,错误:1。禁用:OK(4 测试,6 断言)- 断言被计数,但即使评估返回 FALSE 也通过。
  • 我想知道如何使用远程/步进调试器跟踪 PHPUnit 会话。

标签: php phpunit


【解决方案1】:

我现在可以解决这个问题。这与我升级了 PHPUnit 有关,API 略有变化。我再次将PHPUnit_Framework_Constraint 作为我自己的约束模式,阅读其中的 cmets 会有所帮助。

evaluate 函数现在不同了,我现在将评估逻辑移到了私有函数中,并从 evaluate 切换到 matches。它适用于返回值。 evaluate 默认情况下不再使用返回值,但预计会引发异常。为了充分受益,您还可以实例化一些比较器对象,但这超出了我的想象,您可以在 asserEquals 约束中找到有关它的更多信息。

class ConstraintLastError extends \PHPUnit_Framework_Constraint {

    ...

    /**
     * Evaluates the constraint for parameter $file. Returns TRUE if the
     * constraint is met, FALSE otherwise.
     *
     * This method can be overridden to implement the evaluation algorithm.
     *
     * @param mixed $other Value or object to evaluate.
     * @return bool
     */
    public function matches($file)
    {       
        return $this->compareAgainstLast($file, $this->error);
    }

    /**
     * 
     * @param string $file
     * @param string $error
     * @return bool
     */
    private function compareAgainstLast($file, $error)
    {
        if (!$last = error_get_last())
        {
            $last = array('message' => '(none)', 'file' => '');
        }

        $this->lastError = $last['message'];
        $this->lastFile  = $last['file'];

        return $error === $this->lastError && basename($file) === basename($this->lastFile);
    }

    /**
     * @param string $file
     */
    protected function failureDescription($file)
    {
        return sprintf('the last error is "%s" in %s, was "%s" in %s'
                    , $this->error, basename($file)
                    , $this->lastError, basename($this->lastFile)
                );
    }

    ...

它现在就像一个魅力:

1) FragmentTest::testGetType
Failed asserting that the last error is "Suboptimal State Error" in Fragment.php, was "Invalid State Error" in Fragment.php.

其他人也有类似的问题,但切换到fail 的解决方案不同,您也可以调用它,因为它是在基类Phake Fixes issues #43 and #44 中实现的。

【讨论】:

  • 我觉得不错。顺便说一下,->fail 也用于一些 phpunits 自己的约束:github.com/sebastianbergmann/phpunit/blob/3.6/PHPUnit/Framework/… - 比较器主要用于直接获取 PHP 的 == 行为(请参阅:github.com/sebastianbergmann/phpunit/blob/3.6/PHPUnit/Framework/…),但在您的情况下不需要。感谢分享:)
  • @edorian:感谢您的反馈。你认为向 PHPUnit 提出这样的断言值得吗? (我已经对其进行了一些修改,因此它更有用,例如文件名可选,更关注实际的错误消息)。
  • 我不认为 soa 离开 PHPUnit_Framework_Error_Warning::$enabled 和 * @expectedException PHPUnit_Framework_Error_Warnings * @expectExceptionMessage myExcpetedErrorMessage 对于这些情况是推荐的,并且为用户提供另一个选项不值得额外的复杂性,因为没有多少人真正需要它。我绝对可以看到您的用例,但我认为它不适用于很多人。不过可能是错的.. 总是要为每个人做出判断:) 也许应该在某个时候进行民意调查并提出建议:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-02-19
  • 1970-01-01
  • 1970-01-01
  • 2016-01-14
  • 1970-01-01
  • 1970-01-01
  • 2016-11-23
相关资源
最近更新 更多