Skip to content

Instantly share code, notes, and snippets.

@benlk
Last active March 29, 2023 13:13
Show Gist options
  • Star 50 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save benlk/d1ac0240ec7c44abd393 to your computer and use it in GitHub Desktop.
Save benlk/d1ac0240ec7c44abd393 to your computer and use it in GitHub Desktop.
Collection of notes on WP_UnitTestCase
  1. Table of contents
  2. General information
    1. Terms
    2. General structure of a test
    3. WordPress-specific assertions and test functions
      • enqueues
      • creating posts
      • creating terms
      • attaching images
      • ?
  3. Example unit tests
  4. External resources, which this draws from based upon

Ryan's previous blog post: http://nerds.inn.org/2014/12/23/writing-stupid-simple-unit-tests/

  • what are tests
  • why you should write tests
  • simple test example

Terminology:

  • Test case: a set of conditions that you set up in order to assert expected outcome
  • Test class: A collection of test cases, extending WP_UnitTestCase
  • Test suite: A collection of test classes

General structure of a test:

  1. Arrange: the context, the dependencies
  2. Act: call the method, run the function, trigger the action
  3. Assert: Check for the expected behavior.

To figure out how many assertions you need in a test, figure out how many different paths through the code execution can be taken. Compute the cyclomatic complexity of your function: Add one point to the cyclomatic complexity score for every if, for every case, for every boolean and for every loop (but not every loop thoruhg the loop). You're going to need at least points-many different cases, and enough assertions to check that the cases are correct.

If your cyclomatic complexisty is higher than 7, you should probably refactor the function.

WordPress-specific assertions and test functions:

In addition to the assertions made available by PHPunit, WordPress makes a number h

Covered in some detail here.

  • Navigate to Site URL: (updates globals)
    • $this->get_url($url);
    • $this->go_to($url); // Example: $url = '/2007/'
  • Test WP_Query for Conditionals (is_page, is_single, is_404)
    • $this->assertQueryTrue($arg1, $arg2, ...);
  • Test for Errors
    • $this-assertWPError($thing);

WordPress also provides a number of generators to create things for tests.

  • Generate WordPress data fixtures:
    • Each of these factories has three methods:
      • ->create(); creates an object of that type and then returns the object's ID
      • ->create_and_get(); creates an object of that type, then returns the entire object
      • ->create_many( $n ); creates $n many posts
    • These are the main factories:
      • $this->factory->post
      • $this->factory->attachment
      • $this->factory->comment
      • $this->factory->user
      • $this->factory->term
      • $this->factory->category
      • $this->factory->tag
      • $this->factory->blog
      • $this->factory->network

Generating a post in a series

The post generator's arguments are functionally equivalent to those of wp_insert_post

class TestFunctions extends WP_UnitTestCase {
	function setUp() {
		parent::setUp();

		$this->post = $this->factory->post->create_and_get(array(
			// these args are the same as in wp_insert_post
			// Here's how this works: https://core.trac.wordpress.org/browser/tags/4.1.1/src/wp-includes/post.php#L3377

			'tax_input' => array(
#				'taxonomy' => 'term', // note: slug, not ID
				'series' => 'test'
			)
		));
	}
	// do your tests here
}

Generating multiple posts in a prominence

The post generator's arguments are functionally equivalent to those of wp_insert_post

class TestFunctions extends WP_UnitTestCase {
	function setUp() {
		parent::setUp();

		$this->factory->post->create_many(array(
			// these args are the same as in wp_insert_post
			// Here's how this works: https://core.trac.wordpress.org/browser/tags/4.1.1/src/wp-includes/post.php#L3377

			'tax_input' => array(
#				'taxonomy' => 'term',
				'series' => 'test'
			)
		));
	}
	// do your tests here
}

Creating posts in a category and querying that category

When creating the post, the category id must be in an array(), even if it is singular.

class FeaturedContentTestFunctions extends WP_UnitTestCase {
	function setUp() {
		parent::setUp();
		
		// Let's create a category
		$this->cat1 = $this->factory->term->create(array(
			'taxonomy' => 'category'
		));
		// Let's create some featured posts in a category
		$this->factory->post->create_many(2, array(
			'post_category' => array($this->cat1),
			'tax_input' => array(
#				'taxonomy' => 'term',
				'prominence' => 'taxonomy-featured'
			)
		));
		$this->factory->post->create_many(2, array(
			'post_category' => array($this->cat1),
			'tax_input' => array(
#				'taxonomy' => 'term',
				'prominence' => 'taxonomy-secondary-featured'
			)
		));
		// And some non-featured posts
		$this->cat1_no_prom = $this->factory->post->create_many(2, array(
			'post_category' => array($this->cat1)
		));

	}
	function test_category_posts() {
		$ret = get_posts(array('category' => $this->cat1 ));
	}

Running a test as a different user

I tried this with wp_set_current_user, and it worked for running the test as that user, but all subsequent tests ran as that user. wp_set_current_user doesn't appear to be able to switch back to the default test-running, omniscient, omnipotent user.

Running a test on a different page

Unless otherwise specified, tests run on the homepage of your site, /. If you want to run tests on a different page on your site, such as a post, you can use the go_to() method of WP_UnitTestCase:

class RunTestsElsewhere extends WP_UnitTestCase {
	function on_a_post() {
		$post = $this->factory->post->create();
		$this->go_to('/?p=' . $post ); // /?p=12345 is a guaranteed-to-work link
		// run your test
	}
	function on_a_category() {
		$category = $this-factory->category->create();
		$post = $this->factory->post->create(array(
			'post_category' => $category,
		));
		$this->go_to('/?cat=' . $category); // /?cat
		// run your test
	}
}
@gfirem
Copy link

gfirem commented Jul 10, 2017

Awesome!

@vanduc1102
Copy link

great, thanks

@GaryJones
Copy link

All of the $this->factory->... should be self::factory()->... - see https://core.trac.wordpress.org/changeset/54087.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment