When I get started writing unit tests in WordPress, I continuously ran into what seemed to be strange behaviours by the WordPress Unit Testing framework. Strange when comparing to other PHP unit tests anyway.
It wasn’t until I read all the code of the bootstrap and install scripts, as well as the base WordPress unit test case, that I was able to make sense of it all.
So in this article, I’ll detail how WordPress Unit Testing framework handles fixtures. In addition, I’ll touch on a few things to watch out for when writing your own tests.
Table of Contents
- Database Fixtures
- Hooks (Actions and Filters)
- Fixture Setup During the Bootstrap and Install Scripts
- Automatic Fixture Deletion
- Things to Watch Out For
Database Fixtures
- The only data that persists during all test is that which can be simulated by using hooks in database retrieval functions.
- Options can be persisted by setting them in the
$wp_tests_optionsglobal in thebootstrap.phpfile. - Any fixtures created in
wpSetUpBeforeClass()get deleted automatically bytearDownAfterClass()after all tests in a test class have run. - Any fixtures created in a test get deleted automatically by
tearDownafter the test has run. - Any changes to the database that still exist after having run all tests get reset during the next test run.
Hooks (Actions and Filters)
- Any actions or filters that need to persist during all tests need to be added in the
bootstrap.phpfile. - Any actions or filters that were added in
wpSetUpBeforeClass()need to be reset inwpTearDownAfterClass(). - Any actions or filters that were added in
setUp()get reset automatically duringtearDown().
Fixture Setup During the Bootstrap and Install Scripts
- Each time that the unit tests are run, the database is reset to a fresh install.
- The default post is deleted afterwards.
- The
permalink_structureoption is left empty. - The
$wp_theme_directoriesglobal is set todata/themedir1, and theWP_DEFAULT_THEMEconstant is set todefault. The theme indata/themedir1/defaultis what gets used by default for tests. Thefunctions.phpfile of this theme is not entirely empty, so beware of side-effects. - The
$_SERVERsuperglobal is filled with test data. - The
$phpmailerglobal is set to an instance ofMockPHPMailer(). - The die handler is enabled and set to a function that outputs the die message.
Automatic Fixture Deletion
setupBeforeClass():- Sets PHP’s error reporting to display errors.
- Calls
wpSetUpBeforeClass()if it exists, and passes the instance ofWP_UnitTest_Factorystored in a static property of the test case class as a method argument.
tearDownAfterClass():- Calls
_delete_all_data()which deletes all data except the Uncategorized term and the default user. - Calls the
flush_cache()method, which resets the$wp_object_cacheglobal. - Calls
wptearDownAfterClass()if it exists.
- Calls
setUp():- Retrieves the files in the directory returned by
wp_upload_dir()and stores them in the$ignore_filesproperty. This property is used by theremove_added_uploads()method to reset the uploads folder in certain Core unit tests. - Calls the
_backup_hooks()method to save the current state of the$wp_actions,$wp_filterand$wp_current_filterglobals. - Calls the
clean_up_global_scope()method, which resets the$_GETand$_POSTsuperglobals, and calls theflush_cache()method. - If the
WP_RUN_CORE_TESTSconstant is defined andtrue, post types, custom taxonomies, custom post statuses, the permalink structure, and the$_SERVERsuperglobal are reset between each test. - Starts an SQL transaction, and filters the
queryhook, so that anyCREATE TABLEorDROP TABLEstatements are changed toCREATE TEMPORARY TABLEandDROP TEMPORARY TABLE. - Calls the
expectDeprecated()method, that remaps the Core_deprecated_*()functions to methods inside of the base test class. - Changes the callback of the
wp_die_handlerfilter to theget_wp_die_handler()method, which throws an exception of the typeWPDieExceptionwheneverwp_die()is called.
- Retrieves the files in the directory returned by
tearDown():- Rolls back any changes made to the database.
- On multisite, calls
restore_current_blog()if the tests calledswitch_to_blog(). - Erases the contents of the
$wpand$wp_queryglobals with new instances ofWPandWP_Query. - Sets all globals related to The Loop to
null:$post,$id,$authordata,$currentday,$currentmonth,$page,$pages,$multipage,$more, and$numpages. - Removes theme support for HTML5.
- Removes the
queryandwp_die_handlerfilter callbacks. - Restores the state of the hooks previously saved by
setUp(). - Sets the current user to the default user.
Things to Watch Out For
- If you are running the tests on the same install that you use for local development,
WP_UnitTestCase::setUp()will scan the contents of your local uploads folder. This can make your tests really, really slow. To avoid this, use theupload_dirfilter in the bootstrap file to point to a different uploads directory for the tests. - Any plugins or themes that your tests need, need to be loaded individually.
- MU plugins will get used by your tests if you use your local development install to run the tests.
- If your tests rely on permalink settings, they need to be set in the bootstrap file, or in the tests themselves.