Introduction to Unit Tests

Sal Ferrarello / @salcode

salcode.com/wcm

( Jump to Resources )

Covering Patio Furniture

Speeding Up Iteration

  • Palindrome Example
  • Secret to Writing Easy to Test Code
  • Other Ways to Unit Test
  • My Favorite Palindrome
function palindrome_notify( $content ) {


		return

			  $content;



}

add_filter( 'the_content', 'palindrome_notify' );
function palindrome_notify( $content ) {


		return
			'<div class="alert alert-success">Palindrome!<div> '
			. $content;



}

add_filter( 'the_content', 'palindrome_notify' );
Website with blog post titled 'racecar' (a palindrome) and the 'Palindrome!' notification.
Website with blog post titled 'WordPress Filter Early Return Pattern' (not a palindrome) but the 'Palindrome!' notification still displays.
function palindrome_notify( $content ) {


		return
			'<div class="alert alert-success">Palindrome!<div> '
			. $content;



}

add_filter( 'the_content', 'palindrome_notify' );

function palindrome_notify( $content ) {

	if ( is_palindrome( get_the_title() ) ) {
		return
			'<div class="alert alert-success">Palindrome!<div> '
			. $content;
	}

	return $content;
}

add_filter( 'the_content', 'palindrome_notify' );

function is_palindrome( $str ) {
	return $str === strrev( $str );
}
Two screens correctly showing Palindrome alert on one and no alert on the other
Ship It!

Bugs

function palindrome_tests( $content ) {
  $content = "palindrome_tests()
\n"; return $content; } add_filter( 'the_content', 'palindrome_tests', 12 );
Test heading 'palindrome_tests()' displayed on webpage with no test entries
function palindrome_tests( $content ) {
  $content = "palindrome_tests()
\n"; return $content; } add_filter( 'the_content', 'palindrome_tests', 12 );
function palindrome_tests( $content ) {
  $content = "palindrome_tests()
\n"; if ( true === is_palindrome( 'racecar' ) ) { $content .= "✅ racecar
\n"; } else { $content .= "❌ racecar
\n"; } return $content; } add_filter( 'the_content', 'palindrome_tests', 12 );
racecar passing test displayed on webpage
function palindrome_tests( $content ) {
  $content = "palindrome_tests()
\n"; if ( true === is_palindrome( 'racecar' ) ) { $content .= "✅ racecar
\n"; } else { $content .= "❌ racecar
\n"; } return $content; } add_filter( 'the_content', 'palindrome_tests', 12 );
  if ( true === is_palindrome( 'racecar' ) ) {
		$content .= "✅ racecar 
\n"; } else { $content .= "❌ racecar
\n"; }
  if ( true === is_palindrome( 'racecar' ) ) {
		$content .= "✅ racecar 
\n"; } else { $content .= "❌ racecar
\n"; } if ( false === is_palindrome( 'salcode' ) ) { $content .= "✅ salcode
\n"; } else { $content .= "❌ salcode
\n"; }
salcode passing test displayed on webpage
  if ( true === is_palindrome( 'racecar' ) ) {
		$content .= "✅ racecar 
\n"; } else { $content .= "❌ racecar
\n"; } if ( false === is_palindrome( 'salcode' ) ) { $content .= "✅ salcode
\n"; } else { $content .= "❌ salcode
\n"; }
	$content .= test_is_palindrome('racecar', true);
	$content .= test_is_palindrome('salcode', false);


function test_is_palindrome( $str, $expected ) {
	if ( $expected === is_palindrome( $str ) ) {
		return "✅ ${str} 
\n"; } else { return "❌ ${str}
\n"; } }
	$content .= test_is_palindrome('racecar', true);
	$content .= test_is_palindrome('salcode', false);
	$content .= test_is_palindrome('Anna', true);

function test_is_palindrome( $str, $expected ) {
	if ( $expected === is_palindrome( $str ) ) {
		return "✅ ${str} 
\n"; } else { return "❌ ${str}
\n"; } }
Anna failing test displayed on webpage

function is_palindrome( $str ) {

	return $str    === strrev( $str    );
}

function is_palindrome( $str ) {
	$lc_str = strtolower( $str );
	return $lc_str === strrev( $lc_str );
}
Anna passing test displayed on webpage

is_palindrome() is easy to test because it is simple and pure

is_palindrome() is easy to test because it is simple and pure

Pure Function


f(x) = 2x + 3
x f(x)
03
15
513

Pure Function


function f($x) {
	return 2 * $x + 3;
}

function replace_gutenberg($str) {
	return str_replace(
		'Gutenberg',
		'the block editor',
		$str
	);
}

Not Pure Function


rand(0, 10);

Not Pure Function


function get_num_comments_per_page() {
	return get_option('comments_per_page');
}

function get_wp_version() {
	global $wp_version;
	return $wp_version;
}

Not Pure Function


function palindrome_notify( $content ) {

	if ( is_palindrome( get_the_title() ) ) {
		return
			'<div class="alert alert-success">Palindrome!<div> '
			. $content;
	}

	return $content;
}

add_filter( 'the_content', 'palindrome_notify' );

Pure Function


function is_palindrome( $str ) {
	$lc_str = strtolower( $str );
	return $lc_str === strrev( $lc_str );
}

Secret to Code that is Easy to Unit Test

Pure functions

I Need Functions that are Not Pure

Yes


function palindrome_notify( $content ) {

	if ( is_palindrome( get_the_title() ) ) {
		return
			'<div class="alert alert-success">Palindrome!<div> '
			. $content;
	}

	return $content;
}

add_filter( 'the_content', 'palindrome_notify' );

Extract functionality into pure functions

Pure Functions

  • Easier to write unit tests
  • My code is more organized
  • Allows me to reuse functions
  • Helps me catch bugs sooner
  • Refactoring is easier

Other Ways to Unit Test

Command Line


$ php tests/cli-test-is-palindrome.php

✅ racecar
✅ salcode
✅ Anna
include __DIR__ . '/is-palindrome.php';

test_is_palindrome('racecar', true);
test_is_palindrome('salcode', false);
test_is_palindrome('Anna', false);

function test_is_palindrome( $str, $expected ) {
	if ( $expected === is_palindrome( $str ) ) {
		echo "✅ ${str}\n";
	} else {
		echo "❌ ${str}\n";
	}
}

Command Line


$ php tests/cli-test-is-palindrome.php

✅ racecar
✅ salcode
✅ Anna

PHPUnit

PHPUnit is a testing framework for PHP.

PHPUnit


$ composer install
$ ./vendor/bin/phpunit
PHPUnit 11.1.3 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.2.17
Configuration: /palindrome-notify/phpunit.xml.dist

......                               3 / 3 (100%)

Time: 00:00.019, Memory: 8.00 MB

OK (3 tests, 3 assertions)
	#[DataProvider('isPalindromeProvider')]
	public function testIsPalindrome( $str, $expected ) {
			$this->assertEquals(
					$expected, is_palindrome( $str )
			);
	}

	public static function isPalindromeProvider() {
		return [
			[ 'racecar', true ],
			[ 'salcode', false ],
			[ 'Anna', true ],
		];
	}

Pure Functions

Favorite Palindrome

Go hang a salami, I'm a lasagna hog

Resources

WebDevStudios

Sal Ferrarello

salcode.com/wcm

Image Credits