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
- 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_options
global in thebootstrap.php
file. - 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
tearDown
after 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.php
file. - 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_structure
option is left empty. - The
$wp_theme_directories
global is set todata/themedir1
, and theWP_DEFAULT_THEME
constant is set todefault
. The theme indata/themedir1/default
is what gets used by default for tests. Thefunctions.php
file of this theme is not entirely empty, so beware of side-effects. - The
$_SERVER
superglobal is filled with test data. - The
$phpmailer
global 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_Factory
stored 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_cache
global. - Calls
wptearDownAfterClass()
if it exists.
- Calls
setUp()
:- Retrieves the files in the directory returned by
wp_upload_dir()
and stores them in the$ignore_files
property. 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_filter
and$wp_current_filter
globals. - Calls the
clean_up_global_scope()
method, which resets the$_GET
and$_POST
superglobals, and calls theflush_cache()
method. - If the
WP_RUN_CORE_TESTS
constant is defined andtrue
, post types, custom taxonomies, custom post statuses, the permalink structure, and the$_SERVER
superglobal are reset between each test. - Starts an SQL transaction, and filters the
query
hook, so that anyCREATE TABLE
orDROP TABLE
statements are changed toCREATE TEMPORARY TABLE
andDROP 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_handler
filter to theget_wp_die_handler()
method, which throws an exception of the typeWPDieException
wheneverwp_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
$wp
and$wp_query
globals with new instances ofWP
andWP_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
query
andwp_die_handler
filter 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_dir
filter 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.