phpunit:避免打印很长的输出

Posted

技术标签:

【中文标题】phpunit:避免打印很长的输出【英文标题】:phpunit: Avoid printing very long output 【发布时间】:2020-04-29 09:26:39 【问题描述】:

我有一个 phpUnit 测试,它检查渲染的 html 输出不包含某个字符串,我使用:

public function testSomething() 
    $htmlOutput = ...;
    self::assertDoesNotMatchRegularExpression(
        '/...pattern to detect a certain error.../',
        $htmlOutput,
        'HTML response contained a certain error',
    );

当测试失败时,PHPUnit 会打印一个超长的输出:

There was 1 failure:

1) MyTest::testSomething
HTML response contained a certain error
Failed asserting that '<!DOCTYPE html>\r\n
<html lang="en">\r\n
<head>\r\n
...
... hundreds and hundreds of lines
....
</body>\r\n
</html>' does not match PCRE pattern "/...pattern to detect a certain error.../".

这很烦人,因为所有重要信息现在都在我的终端中向上滚动,无法触及,这是失败测试的名称和实际消息“HTML 响应包含某个错误”。当然,确切的字符串对于找出问题所在可能很重要,但在一半的情况下,消息已经足够好了。

这里推荐的方法是什么?

【问题讨论】:

【参考方案1】:

恐怕使用assertDoesNotMatchRegularExpression()时就是这个样子。话虽如此,我建议不要使用正则表达式来验证 HTML 或 XML。改用使用 CSS 选择器或 XPath 表达式的专用断言。

【讨论】:

您对不使用 HTML 的正则表达式是绝对正确的,我将寻找更好的替代方案来解析 HTML/XML(并因此断言其有效结构)。然而,并非我的所有输出都是 HTML 或 XML,但我找到了一个替代方案,我将很快发布作为附加答案。【参考方案2】:

正如 Sebastian Bergmann 所指出的,通常 HTML 和 XML 验证不应该使用正则表达式来完成。我发现 PHP 的带有 xpath 查询的 XML 解析器很有用。此外,框架通常包含有用的 PHPUnit 扩展(例如 symfony)。

也就是说,我确实找到了一个即使对于非 HTML 内容也能很好地工作的解决方案,例如长纯文本输出。它涉及编写自定义 PHPUnit 约束:

use PHPUnit\Framework\Constraint\Constraint;

/**
 * Class RegularExpressionForLongString is a variant of PHPUnit's RegularExpression that
 * does not print the entire string on failure, which makes it useful for testing very
 * long strings.  Instead it prints the snippet where the regex first matched.
 */
class RegularExpressionForLongString extends Constraint 
    /**
     * Maximum length to print
     */
    private const MAX_LENGTH = 127;

    /**
     * @var string
     */
    private $pattern;

    /**
     * @var array|null
     */
    private $lastMatch = null;

    /**
     * RegularExpressionForLongString constructor.
     *
     * @param string $pattern
     */
    public function __construct(string $pattern) 
        $this->pattern = $pattern;
    

    /**
     * @inheritDoc
     */
    public function toString(): string 
        return sprintf(
            'matches PCRE pattern "%s"',
            $this->pattern
        );
    

    /**
     * @inheritDoc
     */
    protected function matches($other): bool 
        return preg_match($this->pattern, $other, $this->lastMatch, PREG_OFFSET_CAPTURE) > 0;
    

    /**
     * @inheritDoc
     */
    protected function failureDescription($other): string 
        if (!is_string($other)) 
            return parent::failureDescription($other);
        

        $strlen = strlen($other);
        $from = $this->lastMatch[0][1];
        $to = $from + strlen($this->lastMatch[0][0]);
        $context = max(0, intdiv(self::MAX_LENGTH - ($to - $from), 2));
        $from -= $context;
        $to += $context;
        if ($from <= 0) 
            $from = 0;
            $prefix = '';
         else 
            $prefix = "\u2026";
        
        if ($to >= $strlen) 
            $to = $strlen;
            $suffix = '';
         else 
            $suffix = "\u2026";
        

        $substr = substr($other, $from, $to - $from);
        return $prefix . $this->exporter()->export($substr) . $suffix . ' ' . $this->toString();
    

然后在一个新的基类中进行测试:

use PHPUnit\Framework\Constraint\LogicalNot;

/**
 * Class MyTestCase
 */
class MyTestCase extends TestCase 
    /**
     * Asserts that a string does not match a given regular expression.  But don't be so verbose
     * about it.
     *
     * @param string $pattern
     * @param string $string
     * @param string $message
     */
    public static function assertDoesNotMatchRegularExpressionForLongString(string $pattern, string $string, string $message = ''): void 
        static::assertThat(
            $string,
            new LogicalNot(new RegularExpressionForLongString($pattern)),
            $message,
        );
    


这是一个如何使用它的示例:

self::assertDoesNotMatchRegularExpressionForLongString('/\[A-Z_]+\/', $content, "Response contains placeholders that weren't substituted");

这是失败的示例输出:

There was 1 failure:

1) <namespace>\SomeClassTest::testFunc
Response contains placeholders that weren't substituted
Failed asserting that …'re will be context printed here\r\n
    CLIENT_FIRST_NAME\r\n
    Some other text here.\r\n
    '… does not match PCRE pattern "/\[A-Z_]+\/".

【讨论】:

以上是关于phpunit:避免打印很长的输出的主要内容,如果未能解决你的问题,请参考以下文章

phpunit 避免模拟的构造函数参数

markdown [漂亮的打印机] Phpunit Pretty Printer安装#phpunit #laravel #prettyprinter #intsall #php

Laravel 5.3 - 避免在 phpunit 测试中发送松弛通知

如何使用 phpunit 模拟 Yii@1.1 的 CActiveRecord(避免空属性)?

PHPUnit - 啥都不做,没有错误,没有输出

PHPunit - 错误