正如 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 = "\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 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_]+\}/".