phpunit:避免打印很长的输出



我有一个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>rn
<html lang="en">rn
<head>rn
...
... hundreds and hundreds of lines
....
</body>rn
</html>' does not match PCRE pattern "/...pattern to detect a certain error.../".

这非常令人讨厌,因为所有重要信息现在都在我的终端中向上滚动,无法访问,这就是失败测试的名称和实际消息"HTML响应包含特定错误"。当然,确切的字符串对于找出问题可能很重要,但在一半的情况下,消息就足够好了。

这里推荐的方法是什么?

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

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

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

use PHPUnitFrameworkConstraintConstraint;
/**
* 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 = "u{2026}";
}
if ($to >= $strlen) {
$to = $strlen;
$suffix = '';
} else {
$suffix = "u{2026}";
}
$substr = substr($other, $from, $to - $from);
return $prefix . $this->exporter()->export($substr) . $suffix . ' ' . $this->toString();
}
}

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

use PHPUnitFrameworkConstraintLogicalNot;
/**
* 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 herern
{CLIENT_FIRST_NAME}rn
Some other text here.rn
'… does not match PCRE pattern "/{[A-Z_]+}/".

最新更新