{"id":166555,"date":"2017-08-12T13:00:00","date_gmt":"2017-08-12T13:00:00","guid":{"rendered":"https:\/\/premium.wpmudev.org\/blog\/?p=166555"},"modified":"2017-08-02T07:30:23","modified_gmt":"2017-08-02T07:30:23","slug":"unit-testing-wordpress-plugins-phpunit","status":"publish","type":"post","link":"https:\/\/wpmudev.com\/blog\/unit-testing-wordpress-plugins-phpunit\/","title":{"rendered":"Unit Testing WordPress Plugins with PHPUnit"},"content":{"rendered":"<p>When you\u2019re working on plugins and you start introducing new functionality, it\u2019s good practice to ensure the existing logic isn\u2019t broken. Without proper testing, the chances are high that simple fixes may snowball into a spaghetti coding nightmare.<\/p>\n<p>So in today\u2019s article, I\u2019ll show you how to take advantage of unit testing for your WordPress plugins. While there are many testing frameworks out there, we\u2019ll stick with PHPUnit as it\u2019s the official framework of choice for WordPress.<\/p>\n<p>WordPress unit tests using PHPUnit are generally geared towards plugins, but there may be times when you\u2019d want to use them for themes. However, as a general rule, your theme shouldn\u2019t offer plugin-like functionality.<\/p>\n<p>We\u2019ll briefly look at the setup, characteristics of unit tests in WordPress and, finally, test a simple plugin.<\/p>\n<p><strong>Note: This article is for advanced WordPress developers. However, if you are a beginner or intermediate user, the concepts discussed here may help you understand what goes on behind the scenes when creating a WordPress application.<\/strong><\/p>\n<p>You will require a working knowledge of WordPress, PHP, and PHPUnit before diving into this post. If you\u2019re unsure or would like a refresher I recommend you read through the following:<\/p>\n<ul>\n<li style=\"font-weight: 400;\"><a href=\"https:\/\/wpmudev.com\/blog\/wordpress-development-beginners-getting-started\/\" target=\"_blank\">WordPress Development for Beginners: Getting Started<\/a><\/li>\n<li style=\"font-weight: 400;\"><a href=\"https:\/\/wpmudev.com\/blog\/wordpress-development-beginners-php\/\" target=\"_blank\">WordPress Development for Beginners: Learning PHP<\/a><\/li>\n<li style=\"font-weight: 400;\"><a href=\"https:\/\/wpmudev.com\/blog\/theme-development\/\" target=\"_blank\">WordPress Development for Beginners: Building Themes<\/a><\/li>\n<li style=\"font-weight: 400;\"><a href=\"https:\/\/wpmudev.com\/blog\/wordpress-development-beginners-widgets-menus\/\" target=\"_blank\">WordPress Development for Beginners: Widgets and Menus<\/a><\/li>\n<li style=\"font-weight: 400;\"><a href=\"https:\/\/wpmudev.com\/blog\/wordpress-development-beginners-building-plugins\/\" target=\"_blank\">WordPress Development for Beginners: Building Plugins<\/a><\/li>\n<li style=\"font-weight: 400;\"><a href=\"https:\/\/phpunit.de\/getting-started.html\" target=\"_blank\">Getting Started with PHPUnit<\/a><\/li>\n<\/ul>\n<h2><b>Unit Testing: Are We On the Same Page?<\/b><\/h2>\n<p>More than anything, unit testing is about improving the quality of your software. The goal is to reduce bugs using a systematic approach. You save time and effort by running test suites for different areas of your application and can be confident about your changes.<\/p>\n<p>From a technical perspective, it\u2019s about writing additional code to test your functions or modules in isolation. It involves passing in input data for different scenarios, boundary conditions and ensuring that the output or returned data is always as expected. The function under test is cut off from the rest of the system. It is different from an integration test where we test how components or functions work together. When you introduce new changes, you only need to run your previous tests. What worked before should continue to work and you\u2019ll know immediately if it didn\u2019t.<\/p>\n<p><b>How this helps you the developer<\/b><\/p>\n<p>The secret behind successful unit tests is to write smaller, less complex functions or modules in your main application. This makes it possible to test particular components in isolation. Each function should perform a single task and perform it well. This way you neither hack your way through with several conditional statements nor end up with spaghetti code. You certainly end up writing more code, but code that is more readable and of a higher quality. And it\u2019s inline with the DRY (Don\u2019t Repeat Yourself) principle of coding:<\/p>\n<blockquote><p>&#8220;Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.&#8221;<\/p><\/blockquote>\n<p>Now that we\u2019re on the same page, let\u2019s get started.<\/p>\n<h2><b>Prerequisites<\/b><\/h2>\n<p>To get started, you\u2019ll need three things:<\/p>\n<ul>\n<li style=\"font-weight: 400;\"><a href=\"http:\/\/phpunit.de\/getting-started.html\" target=\"_blank\">PHPUnit<\/a> &#8211; A library to test PHP code<\/li>\n<li style=\"font-weight: 400;\"><a href=\"http:\/\/wp-cli.org\/\" target=\"_blank\">WP-CLI<\/a> &#8211; The WordPress Command Line interface to integrate your tests with a WordPress install<\/li>\n<li style=\"font-weight: 400;\">A WordPress setup.<\/li>\n<\/ul>\n<p>I recommend you use a development environment like <a href=\"https:\/\/github.com\/Varying-Vagrant-Vagrants\/VVV\" target=\"_blank\">VVV<\/a> as setting up the above will take a while and you don\u2019t want to bloat your everyday work environment. Check out our post <a href=\"https:\/\/wpmudev.com\/blog\/vvv-wordpress-development\/\" target=\"_blank\">Setting Up VVV for WordPress Development<\/a> for details on how to use VVV.<\/p>\n<p><b>A note for Windows users:<\/b><\/p>\n<p>There\u2019s no problem with installing Vagrant and VVV on Windows. And if you\u2019re on Microsoft Windows 10 you can kick it up a notch \u2013 Microsoft released the Windows Subsystem for Linux (WSL), so you can now have the Ubuntu BASH on Windows. This will allow you to run (almost) all native Linux commands on Windows as well as interact with files on your Windows system. MAC and Linux users always had the shell integrated.<\/p>\n<p>I\u2019ll be using the WSL just to prove that it really works and you no longer need to install a separate VM to get the shell. <a href=\"https:\/\/msdn.microsoft.com\/en-us\/commandline\/wsl\/about\" target=\"_blank\">Here\u2019s how you can set up WSL<\/a>.<\/p>\n<h2><b>Development Workflow<\/b><\/h2>\n<p>I am using NetBeans with Vagrant and VVV on the Windows Subsystem for Linux. This gives me a great workflow where I can develop and debug with NetBeans on my host system, and run tests and servers using the VVV virtual environment.<\/p>\n<p>Everything is auto synced between the host and the guest systems. So any tests that I write using NetBeans or any editor in my host are synced to the VM as well.<\/p>\n<p>I also keep my plugins and themes outside the WordPress setups and load them in the target VVV WordPress instance using the CustomFile configuration in Vagrant.<\/p>\n<p>This allows me to use Git (version control) without having to sync unnecessary WordPress core files.<\/p>\n<p>Here\u2019s what my setup looks like:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/vvv-folder-structure.png\" alt=\"VVV folder structure\" width=\"600\" height=\"479\" \/><\/p>\n<p>Plugins and themes are in a folder outside WordPress (on the host) and are loaded in the target WordPress install in the VM by using the following <i>CustomFile<\/i> configuration:<\/p>\n<div class=\"gist\" data-gist=\"ccca70a8f07cc946f822ddbeb65910b1\" data-gist-file=\"customfile-configuration\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/ccca70a8f07cc946f822ddbeb65910b1.js?file=customfile-configuration\">Loading gist ccca70a8f07cc946f822ddbeb65910b1<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/vagrant-custom-file-600.png\" alt=\"Vagrant CustomFile\" width=\"600\" height=\"482\" \/><\/p>\n<p>I\u2019ll be working with a simple plugin,\u00a0phpunit-demo-plugin, and a WordPress instance &#8220;test.dev&#8221; created using vv create to perform unit tests.<\/p>\n<p>Also, to load the test.dev project in NetBeans along with the plugin outside WordPress, I created a symbolic link to the plugins folder as below:<\/p>\n<div class=\"gist\" data-gist=\"96c6562d6e706bcdc7091d2aaf43adb3\" data-gist-file=\"link-to-plugins-folder\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/96c6562d6e706bcdc7091d2aaf43adb3.js?file=link-to-plugins-folder\">Loading gist 96c6562d6e706bcdc7091d2aaf43adb3<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p>Note<b>: <\/b>You need to create symbolic links from within the host machine:<\/p>\n<ul>\n<li style=\"font-weight: 400;\">For Linux\/MAC use <code>ln -s target link_name<\/code><\/li>\n<li style=\"font-weight: 400;\">For Windows use command line to run <code>mklink \/D link_name target<\/code><\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/netbeans-setup-1.png\" alt=\"Netbeans setup\" width=\"600\" height=\"291\" \/><\/p>\n<h2><b>Setting up PHPUnit for Your WordPress Plugin<\/b><\/h2>\n<p>With VVV we have all the necessary tools: PHP, PHPUnit, WP-CLI etc. WP-CLI also installs a base configuration for the testing framework, which is useful to run tests on the WordPress core files.<\/p>\n<p>To extend this for plugins requires a few additional steps. It boils down to three commands:<\/p>\n<ul>\n<li style=\"font-weight: 400;\"><code>&gt;$cd \/srv\/www\/wordpress-installation<\/code><\/li>\n<li style=\"font-weight: 400;\"><code>&gt;$wp scaffold plugin-tests your_plugin<\/code><\/li>\n<li style=\"font-weight: 400;\"><code>&gt;$sh \u00a0wp-content\/plugins\/your_plugin\/bin\/install-wp-tests.sh \u00a0&lt;db_args&gt;<\/code><\/li>\n<\/ul>\n<p>PHPUnit will overwrite the WordPress database so it\u2019s best to use a fresh installation or create a backup of your existing one. Needless to say, don\u2019t run it in your production environment.<\/p>\n<p>I\u2019ll refer to folders as directories when working in the Linux VM.<\/p>\n<p>Let\u2019s walk through this in detail. You can also refer to the <a href=\"https:\/\/github.com\/wp-cli\/wp-cli\/wiki\/Plugin-Unit-Tests\" target=\"_blank\">WP-CLI handbook<\/a>.<\/p>\n<p><b>Step#1<\/b><\/p>\n<p>To set up PHPUnit with your WordPress plugin, start your Vagrant instance with <code>vagrant up<\/code> and then ssh into it with <code>vagrant ssh<\/code><b>. <\/b>Once you\u2019re in, navigate to the root directory of your WordPress installation.<\/p>\n<p>In my case, I\u2019m working with the WordPress installation <b>test.dev<\/b><\/p>\n<p><code>&gt;$cd \/srv\/www\/test\/htdocs\/<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/navigate-to-wp-install.png\" alt=\"Navigate to WordPress install\" width=\"600\" height=\"256\" \/><\/p>\n<p><b>Step#2<\/b><\/p>\n<p>Your plugin (from your host machine) should already be available in the wp-contents directory. To configure unit tests for the plugin, we need to use the WP-CLI\u2019s scaffold command as below:<\/p>\n<p><code>wp scaffold plugin-tests your_plugin<\/code><\/p>\n<p><code>&gt;$wp scaffold plugin-tests phpunit-demo-plugin<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/run-wp-scaffold.png\" alt=\"Run WP scaffold\" width=\"600\" height=\"203\" \/><\/p>\n<p>This is what just happened:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/cat-test-sample-600.png\" alt=\"Cat test sample\" width=\"600\" height=\"568\" \/><\/p>\n<p>If you now check your plugin\u2019s directory, you\u2019ll notice a few additional files, the most important being in the test subdirectory. PHPUnit will automatically run any tests (files prefixed with <code>test-<\/code>) it finds under the tests directory. The auto discovery happens through the <em>phpunit.xml<\/em>, which is the main manifest file that tells PHPUnit where to find the tests and how things are setup.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/phpunit-xml-600.png\" alt=\"PHP Unit XML\" width=\"600\" height=\"400\" \/><\/p>\n<p><b>Step#3<\/b><\/p>\n<p>We now only need to set up a testing copy of the database for our plugin. When tests are run, PHPUnit will use this for our test environment. If you wish to use a working db instance, be sure to back it up.<\/p>\n<p>We need to execute the <em>install-wp-tests.sh<\/em> script located under &#8220;bin\/&#8221; that was also created by the wp scaffold command:<\/p>\n<p><code>bash plugin_dir\/bin\/install-wp-tests.sh db_name db_user db_password db_host version<\/code><\/p>\n<ul>\n<li style=\"font-weight: 400;\"><code>db_name<\/code> is the name of the test database (all data will be deleted!)<\/li>\n<li style=\"font-weight: 400;\"><code>db_user<\/code> is the MySQL user name<\/li>\n<li style=\"font-weight: 400;\"><code>db_pass<\/code>\u00a0 is the MySQL user password<\/li>\n<li style=\"font-weight: 400;\"><code>db_host<\/code> is the MySQL server host<\/li>\n<li style=\"font-weight: 400;\">Version is the WordPress version<\/li>\n<\/ul>\n<p><code>&gt;$bash bin\/install-wp-tests.sh wordpress_test root root localhost latest<b><\/b><\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/install-wp-tests-600.png\" alt=\"Install WP tests\" width=\"600\" height=\"518\" \/><\/p>\n<p><b>Step#4<\/b><\/p>\n<p>To test if everything installed correctly, all you need to do is run the command <code>phpunit<\/code>. A sample test file <em>test-sample.php<\/em> that was created earlier will be executed.<\/p>\n<p><code>&gt;$phpunit<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/phpunit-install-test.png\" alt=\"PHPUnit install test\" width=\"600\" height=\"440\" \/><\/p>\n<h2><b>Writing Unit Tests for WordPress<\/b><\/h2>\n<p>If you look at the included <em>test-sample.php<\/em> under tests, you\u2019ll notice that the class SampleTest extends <code>WP_UnitTestCase<\/code> and not <code>PHPUnit_Framework_TestCase<\/code>. That\u2019s because WordPress ships with its own <a href=\"https:\/\/core.trac.wordpress.org\/browser\/trunk\/tests\/phpunit\/includes\/testcase.php\" target=\"_blank\">testing library<\/a> that offers WordPress specific functionality and is built on top of PHPUnit_Framework_TestCase.<\/p>\n<p>With WP_UnitTestCase, every method that begins with <code>test<\/code> will be run automatically.<\/p>\n<p>When we ran <code>phpunit<\/code> above, the <code>test_sample()<\/code> was executed as it was prefixed with <code>test_<\/code> and asserted that <code>True<\/code> was\u00a0true.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/wp-unit-test-case.png\" alt=\"WP unit test case\" width=\"600\" height=\"512\" \/><\/p>\n<p>Here\u2019s how we can make use of <code>WP_UnitTestCase<\/code> for our own tests:<\/p>\n<p><code>WP_UnitTestCase<\/code> provides us with Object Factories, Utility Methods, and WordPress specific assertions in addition to <a href=\"https:\/\/phpunit.de\/manual\/current\/en\/appendixes.assertions.html\" target=\"_blank\">assertions provided by PHPUnit<\/a>,<\/p>\n<p><b>WordPress Test Assertions<\/b><\/p>\n<ul>\n<li style=\"font-weight: 400;\">Assertions for Errors\n<ul>\n<li style=\"font-weight: 400;\"><code>$this-&gt;assertWPError($actual, $message)<\/code><\/li>\n<li style=\"font-weight: 400;\"><code>$this-&gt;assertNotWPError($actual, $message)<\/code><\/li>\n<li style=\"font-weight: 400;\"><code>$this-&gt;assertIXRError($actual, $message)<\/code><\/li>\n<li style=\"font-weight: 400;\"><code>$this-&gt;assertNotIXRError($actual, $message)<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<ul>\n<li style=\"font-weight: 400;\">Assertions to test WP_Query for conditional tags\n<ul>\n<li style=\"font-weight: 400;\"><code>$this-&gt;assertQueryTrue($args)<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>E.g. <code>$this-&gt;assertQueryTrue('is_single', 'is_feed')<\/code> means <code>is_single()<\/code> and <code>is_feed()<\/code> must be true to pass.<\/p>\n<p><b>WordPress Object Factories<\/b><\/p>\n<p><a href=\"https:\/\/core.trac.wordpress.org\/browser\/trunk\/tests\/phpunit\/includes\/factory\" target=\"_blank\">Factories<\/a> make it very simple to create posts, taxonomies, users, etc. They use the following three methods to create objects:<\/p>\n<ul>\n<li style=\"font-weight: 400;\"><code>create()<\/code> &#8211; returns the object ID of the created object<\/li>\n<li style=\"font-weight: 400;\"><code>create_and_get()<\/code> &#8211; creates and return the entire object<\/li>\n<li style=\"font-weight: 400;\"><code>create_many($count)<\/code> &#8211; creates multiple posts based on $count<\/li>\n<\/ul>\n<p>To create a user and get its user id we can simply do &#8211;<\/p>\n<p><code>$user_id = $this-&gt;factory-&gt;user-&gt;create();<\/code><\/p>\n<p>Or to create a user with a specific WordPress role<\/p>\n<p><code>$user_id = $this-&gt;factory-&gt;user-&gt;create( array( 'role' =&gt; 'author' ) );<\/code><\/p>\n<p>Other factory types include post, attachment, comment, user, term, category, tag, blog, network.<\/p>\n<p><b>Examples of using Factories<\/b><\/p>\n<div class=\"gist\" data-gist=\"8679b9c0d0a8b4beaf118b22096a1e1c\" data-gist-file=\"wordpress-factory-examples.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/8679b9c0d0a8b4beaf118b22096a1e1c.js?file=wordpress-factory-examples.php\">Loading gist 8679b9c0d0a8b4beaf118b22096a1e1c<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p>You can find more details about each factory on the <a href=\"https:\/\/core.trac.wordpress.org\/browser\/trunk\/tests\/phpunit\/includes\/factory\" target=\"_blank\">WordPress Trac<\/a><\/p>\n<p><b>Additional<\/b> <b><code>setUp()<\/code> and <code>tearDown()<\/code><\/b><\/p>\n<p><code>WP_UnitTestCase<\/code> also provides its own <code>setUp()<\/code> and <code>tearDown()<\/code> methods, which can be used with PHPUnit\u2019s <code>setUp()<\/code> and <code>tearDown()<\/code>. With <code>tearDown<\/code>, <code>WP_UnitTestCase<\/code> will reset the WordPress state that may have changed by a test method. And with <code>setUp<\/code> it\u2019ll take care of clearing caches and resetting global variables. To use them, simply call them with <code>parent::setUp()<\/code> or <code>parent::tearDown()<\/code><\/p>\n<p>For example:<\/p>\n<div class=\"gist\" data-gist=\"5932dd52d6e37cb119be9271a938cbfa\" data-gist-file=\"setup-teardown-examples.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/5932dd52d6e37cb119be9271a938cbfa.js?file=setup-teardown-examples.php\">Loading gist 5932dd52d6e37cb119be9271a938cbfa<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p>The WordPress tests run on the root page of your website. To run tests on a different page, you\u2019ll have to instruct it to do so.<\/p>\n<p>For example, to run tests on the Edit Posts Administration screen:<\/p>\n<div class=\"gist\" data-gist=\"d20d0288f9d979d9c217bf5b1cc08984\" data-gist-file=\"run-tests-on-admin-screen.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/d20d0288f9d979d9c217bf5b1cc08984.js?file=run-tests-on-admin-screen.php\">Loading gist d20d0288f9d979d9c217bf5b1cc08984<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p>If you want to test by navigating to a specific url you can make use of <code>$this-&gt;go_to($url)<\/code>, and then test with <code>assertQueryTrue<\/code><\/p>\n<p>For example:<\/p>\n<div class=\"gist\" data-gist=\"5408c143c0e50ffea6619cc9240229cf\" data-gist-file=\"go_to_and_assertquerytrue.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/5408c143c0e50ffea6619cc9240229cf.js?file=go_to_and_assertquerytrue.php\">Loading gist 5408c143c0e50ffea6619cc9240229cf<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<h2><b>Testing Our Plugin (Finally!)<\/b><\/h2>\n<p>Let\u2019s use this information and write some tests for our demo plugin. <a href=\"https:\/\/github.com\/nuancedesignstudio\/phpunit-demo-plugin\" target=\"_blank\">Here\u2019s the link to the phpunit-demo-plugin<\/a>, which adds an additional user meta item when a new user of type \u201cEditor\u201d is created. It also displays the user meta on the profile screens for administrators and the user to modify.<\/p>\n<p>Start by deleting the <em>test-sample.php<\/em> from the tests directory, and create a new file,\u00a0<em>test-phpunit-demo-plugin.php.<\/em><\/p>\n<p>Note: I\u2019ll be writing my tests in NetBeans on my host, and will run them with PHPUnit on the VVV instance. All changes will be auto-synced between the two systems. This way I can take advantage of auto completion, code reference, and debugging using Xdebug with NetBeans.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-1050x1050 size-1050x1050 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/dev-env.png\" alt=\"Development environment\" width=\"1050\" height=\"728\" \/><\/p>\n<p>Now let\u2019s write some tests for our plugin. Here we are creating a user with the role of \u201cauthor\u201d, and checking that the meta key was not created.<\/p>\n<div class=\"gist\" data-gist=\"bdafb2efc50c937ae94b3a3ad416528f\" data-gist-file=\"test-phpunit-demo-plugin.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/bdafb2efc50c937ae94b3a3ad416528f.js?file=test-phpunit-demo-plugin.php\">Loading gist bdafb2efc50c937ae94b3a3ad416528f<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p>When you run the test using phpunit (I\u2019m using <code>phpunit --debug<\/code> for a detailed output) it should pass:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/first-unit-test-600.png\" alt=\"First unit test\" width=\"600\" height=\"330\" \/><\/p>\n<p>Now, let\u2019s create a failing test by creating a user with a role of \u201cEditor,\u201d and then compare the user meta value for <code>preferred_browser<\/code> with an empty string.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/first-failing-test-code.png\" alt=\"First failing test code\" width=\"600\" height=\"247\" \/><\/p>\n<p>On running phpunit the test fails.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/first-failed-test-phpunit.png\" alt=\"First failed test phpunit\" width=\"600\" height=\"572\" \/><\/p>\n<p>Note: When an assertion fails, no other tests are run. You can modify this behavior in <em>phpunit.xml<\/em>.<\/p>\n<p>To fix this, we can either test that the meta value was not empty or perform a string comparison for \u201cchrome\u201d. Also, we\u2019ll split the tests into two functions \u2013 to test users with and without an \u201cEditor\u201d role. This way, by separating the assertions, we have a good model to find bugs as they appear.<\/p>\n<div class=\"gist\" data-gist=\"75668100a9b2864fc8f3e943a7f03046\" data-gist-file=\"test-phpunit-demo-plugin.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/75668100a9b2864fc8f3e943a7f03046.js?file=test-phpunit-demo-plugin.php\">Loading gist 75668100a9b2864fc8f3e943a7f03046<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/passing-test-metakey-chrome.png\" alt=\"Passing test metakey chrome\" width=\"600\" height=\"392\" \/><\/p>\n<p><b>Testing callback or its priority<\/b><\/p>\n<p>In our plugin, we added a callback for the user_register action hook with a priority value different from the default one of 10. Here, we can use the <a href=\"https:\/\/codex.wordpress.org\/Function_Reference\/has_action\" target=\"_blank\"><code>has_action<\/code><\/a> function, which returns the priority of the callback on a specified hook.<\/p>\n<p><b>A failing test as the priority needs to be greater than 10<\/b><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/failing-test-callback-code.png\" alt=\"Failing test callback code\" width=\"600\" height=\"123\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/failing-test-callback-phpunit-600.png\" alt=\"Failing test callback PHPUnit\" width=\"600\" height=\"469\" \/><\/p>\n<p><b>Passing the test with a priority check greater than 10<\/b><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/passing-test-callback-phpunit.png\" alt=\"Passing test callback phpunit\" width=\"600\" height=\"138\" \/><\/p>\n<p><b>Testing form submission<\/b><\/p>\n<p>To test whether the <code>preferred_browser<\/code> field is correctly updated from the user\u2019s profile, we\u2019ll have to spoof the POST variable, and then call the method that updates the user meta. This is testing in isolation. We\u2019re only concerned about our plugin\u2019s function that updates the meta, and not with the rest of the system like if the profile form was submitted correctly or not.<\/p>\n<p><b>A failing test after updating the meta value<\/b><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/failing-test-meta-update-code.png\" alt=\"Failing test meta update code\" width=\"600\" height=\"278\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/failing-test-meta-update-phpunit.png\" alt=\"Failing test meta update phpunit\" width=\"600\" height=\"572\" \/><\/p>\n<p><b>A passing test by comparing that <code>preferred_browser<\/code> was updated to Opera<\/b><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/passing-test-meta-update-code.png\" alt=\"Passing test meta update code\" width=\"600\" height=\"91\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"attachment-600x600 size-600x600 aligncenter\" src=\"https:\/\/wpmudev.com\/blog\/wp-content\/uploads\/2017\/08\/passing-test-meta-update-phpunit.png\" alt=\"Passing test meta update phpunit\" width=\"600\" height=\"465\" \/><\/p>\n<p>Here is the complete code for the tests in this post:<\/p>\n<div class=\"gist\" data-gist=\"3070736ca1d5a60a72a8dbf5cd3d455c\" data-gist-file=\"test-phpunit-demo-plugin.php\"><a class=\"loading\" href=\"https:\/\/gist.github.com\/3070736ca1d5a60a72a8dbf5cd3d455c.js?file=test-phpunit-demo-plugin.php\">Loading gist 3070736ca1d5a60a72a8dbf5cd3d455c<\/a><div class=\"gist-consent-notice\" style=\"display:none\"><p>Please <a href=\"javascript:Cookiebot.renew()\">update your cookie preferences<\/a> to enable preference cookies to view this gist.<\/p><\/div><\/div>\n<h2><b>Real World Examples of Unit Tests in WordPress<\/b><\/h2>\n<p><a href=\"https:\/\/github.com\/nuancedesignstudio\/nds-user-meta-manager\/tree\/master\/tests\" target=\"_blank\">Here are some tests<\/a> I wrote for a simple admin plugin utility that uses object-oriented constructs.<\/p>\n<p>Note: To take advantage of PHPUnit TestDoubles in a plugin without a class you\u2019ll need to use namespaces, and PHP 5.3 or above.<\/p>\n<p>For some advanced test examples check out:<\/p>\n<ul>\n<li style=\"font-weight: 400;\"><a href=\"https:\/\/core.trac.wordpress.org\/browser\/trunk\/tests\/phpunit\/tests\" target=\"_blank\">Tests for the WordPress core<\/a><\/li>\n<li style=\"font-weight: 400;\"><a href=\"https:\/\/github.com\/woocommerce\/woocommerce\/tree\/master\/tests\" target=\"_blank\">WooCommerce Unit Tests<\/a><\/li>\n<li style=\"font-weight: 400;\"><a href=\"https:\/\/deliciousbrains.com\/unit-testing-ajax-api-requests-wordpress-plugins\/\" target=\"_blank\">Tests for ajax requests<\/a><\/li>\n<li style=\"font-weight: 400;\"><a href=\"https:\/\/github.com\/10up\/wp_mock\" target=\"_blank\">WordPress API Mocking Framework<\/a><\/li>\n<\/ul>\n<h2><b>Testing a WordPress Theme<\/b><\/h2>\n<p>A simple way to test a WordPress theme is to use <a href=\"https:\/\/codex.wordpress.org\/Theme_Unit_Test\" target=\"_blank\">Theme Unit Test Data.<\/a> We\u2019ve got a <a href=\"https:\/\/wpmudev.com\/blog\/daily-tip-quick-content-creation-for-new-sites-with-the-theme-unit-test\/\" target=\"_blank\">great article that explains how to set it up<\/a>.<\/p>\n<p>If your theme provides plugin-like functionality or you want to make use of PHPUnit you can have a look at Tom McFarlin\u2019s <a href=\"https:\/\/github.com\/tommcfarlin\/Basic-Theme\" target=\"_blank\">Basic Theme<\/a> tests. How you set it up will depend on the structure of your theme.<\/p>\n<h2><b>Test Driven Development (TDD)<\/b><\/h2>\n<p>TDD is a software design paradigm where unit tests are written first, and then the code is written to pass the tests. The idea is to write failing unit tests, and then writing code to pass those tests. The cycle continues until all missing functionality has been added. With each iteration, the code is refactored without changing the behavior. Whether you use TDD or not writing tests as you develop is the key here.<\/p>\n<h2><b>Taking Plugin Unit Tests Further<\/b><\/h2>\n<p>I hope this article helps you get started with PHPUnit tests in WordPress. You could also look at automating your tests using a task runner like gulp or grunt. Finally, you can follow <a href=\"https:\/\/core.trac.wordpress.org\/browser\/trunk\/tests\/phpunit\/tests\" target=\"_blank\">WordPress Trac<\/a>, which will help you write tests using best practices for WordPress.<\/p>\n<h2><b>Additional Resources<\/b><\/h2>\n<ul>\n<li><a href=\"https:\/\/make.wordpress.org\/cli\/2013\/02\/19\/plugin-unit-tests\/\" target=\"_blank\">https:\/\/make.wordpress.org\/cli\/2013\/02\/19\/plugin-unit-tests\/<\/a><\/li>\n<li><a href=\"http:\/\/wordpress.tv\/?s=unit+test\" target=\"_blank\">http:\/\/wordpress.tv\/?s=unit+test<\/a><\/li>\n<li><a href=\"https:\/\/carlalexander.ca\/introduction-wordpress-unit-testing\/\" target=\"_blank\">https:\/\/carlalexander.ca\/introduction-wordpress-unit-testing\/<\/a><\/li>\n<li><a href=\"https:\/\/jtreminio.com\/2013\/03\/unit-testing-tutorial-part-5-mock-methods-and-overriding-constructors\/\" target=\"_blank\">https:\/\/jtreminio.com\/2013\/03\/unit-testing-tutorial-part-5-mock-methods-and-overriding-constructors\/<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>When you\u2019re working on plugins and you start introducing new functionality, it\u2019s good practice to ensure the existing logic isn\u2019t broken. Without proper testing, the chances are high that simple fixes may snowball into a spaghetti coding nightmare. So in today\u2019s article, I\u2019ll show you how to take advantage of unit testing for your WordPress [&hellip;]<\/p>\n","protected":false},"author":573954,"featured_media":167040,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"blog_reading_time":"","wds_primary_category":0,"wds_primary_tutorials_categories":0,"footnotes":""},"categories":[557],"tags":[10789],"tutorials_categories":[],"class_list":["post-166555","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development","tag-testing-environment"],"_links":{"self":[{"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/posts\/166555","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/users\/573954"}],"replies":[{"embeddable":true,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/comments?post=166555"}],"version-history":[{"count":17,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/posts\/166555\/revisions"}],"predecessor-version":[{"id":209662,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/posts\/166555\/revisions\/209662"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/media\/167040"}],"wp:attachment":[{"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/media?parent=166555"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/categories?post=166555"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/tags?post=166555"},{"taxonomy":"tutorials_categories","embeddable":true,"href":"https:\/\/wpmudev.com\/blog\/wp-json\/wp\/v2\/tutorials_categories?post=166555"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}