Skip to main content

assertEqualHTML() in WordPress 6.9: Stop Testing Whitespace

· 5 min read
Victor Jimenez
Senior Product Engineer

WordPress 6.9 added assertEqualHTML(), so a test that fails because of whitespace in rendered HTML no longer has an excuse. I reviewed the implementation and mapped concrete migration patterns for plugin and theme test suites, plus the places where assertSame() should stay.

Tests that fail on formatting, not behavior​

A string equality check on HTML tests the serializer's formatting habits. It does not test what the page does.

"In plugin and theme suites, many HTML tests still use strict string equality and fail on whitespace, indentation, or equivalent tag formatting instead of real behavior regressions."

A test that fails because class came before href has caught a difference no browser cares about. My fix is to compare meaning with assertEqualHTML() and keep strict equality for output where every byte matters.

Context

assertEqualHTML() landed in WordPress core test tools in 6.9. It compares HTML equivalence, not raw string identity. It normalizes whitespace, attribute order and insignificant formatting before comparing, so <a class="btn" href="/docs"> and <a href="/docs" class="btn"> count as equal.

Migration patterns​

Most of these are a one-line change. The biggest win is the tab where a hand-written normalizer disappears.

Before:

tests/test-output.php
$this->assertSame(
'<p class="notice">Saved</p>',
$actual_html
);

After:

tests/test-output.php
$this->assertEqualHTML(
'<p class="notice">Saved</p>',
$actual_html
);

Which assertion goes where​

The choice depends on what the test promises. If it promises DOM meaning, compare meaning. If it promises exact bytes, compare bytes.

AssertionUse when
assertEqualHTML()Rendered markup where DOM meaning matters more than exact serialization
assertSame()Exact escaping, spacing or deterministic serialization matters
assertEqualHTML()The test currently compensates for formatting differences
assertSame()Security assertions that verify exact output

A bridge for suites older than 6.9​

If the suite also runs against WordPress versions before 6.9, wrap the call so the same test works on both.

tests/helpers/compat.php
private function assertHtmlEquivalent( string $expected, string $actual ): void {
if ( method_exists( $this, 'assertEqualHTML' ) ) {
$this->assertEqualHTML( $expected, $actual );
return;
}
// Fallback for pre-6.9
$this->assertSame( trim( $expected ), trim( $actual ) );
}

The fallback only trims, so attribute order still fails on older versions. That is acceptable for a bridge. Remove it when the oldest supported version reaches 6.9.

Reality Check

Do not over-migrate. Swapping every assertion for assertEqualHTML() makes the suite greener and makes escaping bugs invisible, which is a bad trade. Keep assertSame() for escaping, security and exact output contracts, and use assertEqualHTML() for semantics. This two-lane policy keeps the intent of each test explicit.

Rollout order​

Start where the noise is loudest and stop at anything that checks escaping.

Migration checklist​

Full rollout checklist
  • Start with render-heavy tests (render_callback, shortcode output, template helpers).
  • Convert only tests that currently compensate for formatting differences.
  • Keep assertSame() for escaping, security assertions and exact output contracts.
  • Add a short team note: "Use assertEqualHTML() for semantic markup checks".
  • Delete hand-rolled normalization helper functions.
  • Run the full suite after migration to catch tests that silently relied on exact formatting.
  • Document the two-lane policy in your testing guidelines.

What I learned​

  • assertEqualHTML() landed in WordPress core test tools in 6.9 and compares HTML equivalence, not raw string identity.
  • Most of the gain comes from deleting custom normalization code and cutting flaky failures.
  • A two-lane policy (assertEqualHTML() for semantics, assertSame() for exactness) keeps intent explicit and prevents over-migration.
  • My call: this is one of the most underrated testing improvements in recent WordPress history.

References​