Using factory.php outside of trunk for unit testing

I have written a plugin, and am going through the process of setting up some unit tests for it.

Inside of my unit tests I am utilizing the WP_UnitTest_Factory class to help with the process of creating posts, users etc.

$this->factory->post->create( array( 'post_type' => 'announcement' ) );

I am finding that when I run my unit tests on Travis CI, any reference to WP_UnitTest_Factory on versions outside of LATEST fails.

The error being thrown on Travis CI is:

Notice: Trying to get property of non-object in /home/travis/build/build_name/project/tests/test-helper-functions.php on line 12

This is occuring on 4.1, 4.0, 3.9.2 – but not when WP_Version=latest.

Which is referencing the WP_UnitTest_Factory code (above) to create a post.

My thoughts are that the WP_UnitTest_Factory is included in trunk, so when running on LATEST, the class is available.

When running on older versions, the factory class is not present – thus throwing errors.

I did a bit of research and checked out other plugins unit tests, and they seem to avoid using the factory class altogether, and use built in wp functions (wp_insert_post()).

Is there any way to work around this issue, or should I re-write my tests to use built in WP functions instead of the WP_UnitTest_Factory class, so that my tests will pass on older versions of WordPress?

1 Answer
1

The problem is not that the factory class is unavailable, because if that were so you would be getting a fatal “class not found” error. The problem is that you aren’t using your test case properly. This code (and similar) is what is causing the problem:

/**
 * Testing that timeline_express_get_announcement_date() returns what we expect
 */
public function test_timeline_express_get_announcement_date() {
    // Re-instantiate this class
    $helper_functions_test_class = new TE_Helper_Tests;
    // Create some test announcements, passing in some defaults.
    $announcement_id = $helper_functions_test_class->test_create_announcement( '#FF6347', 'fa-plus', '04/04/1989' );
    // Grab the announcement date
    $announcement_date = timeline_express_get_announcement_date( $announcement_id );
    $comparison_date = date_i18n( get_option( 'date_format' ), strtotime( '04/04/1989' ) );
    // $announcement_date = get_post_meta( $announcement_id, 'announcement_date', true );
    $this->assertEquals( $announcement_date, $comparison_date );
}

Here you are constructing a test case yourself, which means that you are skipping all of the set up that PHPUnit usually performs, like calling the test case’s setUp() method (which is where the factory property is set up.

So when you call test_create_acnnouncement() on the class you just constructed, the factory property hasn’t been set up, and that’s why you are getting these errors.

Instead, you should refactor that test (and others like it) to be something like this:

/**
 * Testing that timeline_express_get_announcement_date() returns what we expect
 */
public function test_timeline_express_get_announcement_date() {
    // Create some test announcements, passing in some defaults.
    $announcement_id = $this->test_create_announcement( '#FF6347', 'fa-plus', '04/04/1989' );
    // Grab the announcement date
    $announcement_date = timeline_express_get_announcement_date( $announcement_id );
    $comparison_date = date_i18n( get_option( 'date_format' ), strtotime( '04/04/1989' ) );
    // $announcement_date = get_post_meta( $announcement_id, 'announcement_date', true );
    $this->assertEquals( $announcement_date, $comparison_date );
}

In other words, just skip the part where you create a new instance of the class, and just use the instance that you already have: $this.

So, why doesn’t this error show up when running against the latest version of the test suite? Because the latest version includes a __get() method to provide the factory. So the property doesn’t have to be set up in the latest version of the tests. But don’t think that this is an indicator that you should be manually constructing the testcases as you were doing, that’s not a normal thing.

So to answer your final question, as to whether you should be using the factories or not, the reason that most plugins you’ve seen were using the built-in WordPress functions may just be because they are ignorant of the factories. While you do not have to use the factories, the benefit of them is that you don’t have to make up fake post content, etc., the factory just automatically generates that for you.

Leave a Comment