Unit Tests for WooCommerce Extensions

I am completely new to PHP unit testing, but I decided it was time to learn after discovering a critical bug in a small WooCommerce extension I had built for a client.

The extension I built added a feature that allowed administrators to limit specific coupons to new customers only. I had done some manual testing and made sure that new customers could use the coupon and existing customers could not. But there was a logic bug I missed that prevented existing customers from using any coupons, even ones that did not have the “new customer” restriction.

After finding the bug, I knew there were several use cases I would need to check every time an update was made to the plugin:

  • New customer should be able to apply a coupon
  • New customer should be able to apply a coupon with a “new customer” restriction
  • Existing customer should be able to apply a coupon without a “new customer” restriction
  • Existing customer should *not* be able to apply a coupon with the “new customer” restriction

Obviously, checking this manually each time would be rather tedious- which is why I turned to unit tests.

Getting Started with WooCommerce Unit Testing

A great advantage to building unit tests for your own WooCommerce extension is that a lot of this code has already been written to support WooCommerce itself. In fact, if you copy the WooCommerce test suite into your extension folder and change a few lines of code you can easily run all of the base WooCommerce tests with your own extension also loaded.

There’s also great documentation if you’re setting up unit tests for the first time.

There might be a better way to do this, but to test my extension I simply copied over the WooCommerce unit test folder and added another variable at the top of bootstrap.php for loading the WooCommerce path:

/** @var string woocommerce plugin directory */
public $woocommerce_plugin_dir;

Defined it in __construct:

$this->woocommerce_plugin_dir   = dirname( dirname( $this->tests_dir ) ) . '/woocommerce';

And then altered load_wc to require both WooCommerce and my extension:

require_once( $this->woocommerce_plugin_dir . '/woocommerce.php' );
require_once( $this->plugin_dir . '/new-customer-coupons.php' );

WooCommerce Unit Testing Helper Functions

By copying over the WooCommerce core unit tests, you also get access to a number of useful helper functions that will help in your tests- such as easily creating test customers, example orders, and example coupons.

If you read through all the WooCommerce core unit tests you should be able to see how the functions are used to simulate the majority scenarios that would need to be tested against in your own extension.

Create a Custom Unit Test

To create tests for my extension, I added a new file to the unit-tests folder. This file gets auto-loaded and runs all the custom tests that are defined there.

There aren’t a lot of tutorials or code examples out there, so I wanted to share how I set up the test for an existing customer attempting to use a coupon with the restriction.

1. Create the customer:

// Create a customer
$email = 'customer@example.org';
$customer = wc_create_new_customer( $email, $email, 'password' );
wp_set_current_user( $customer );

2. Create an order and apply it to new customer. Then set the order to complete (thereby making the new customer an existing customer):

// Create an order and apply it to new customer
$order = \WC_Helper_Order::create_order();
update_post_meta( $order->id, '_customer_user', $customer );
update_post_meta( $order->id, '_billing_email', $email );
$order->update_status( 'wc-completed' );

3. Create a new coupon and set it to have the “new customer” restriction:

// Create coupon
$coupon = \WC_Helper_Coupon::create_coupon();
update_post_meta( $coupon->id, 'new_customers_only', 'yes' );

4. Apply the coupon to the cart and verify that the coupon is not valid when applied:

// Add coupon, test return statement
$this->assertFalse( WC()->cart->add_discount( $coupon->code ) );

5. Verify that 0 coupons have been applied to the cart:

// Test if total amount of coupons is 0
$this->assertEquals( 0, count( WC()->cart->get_applied_coupons() ) );

6. Cleanup after the test runs:

// Remove coupons
WC()->cart->remove_coupons();
// Delete coupon
\WC_Helper_Coupon::delete_coupon( $coupon->id );
// Delete order
\WC_Helper_Order::delete_order( $order->id );
// Delete customer
wp_delete_user( $email );

You can view the full source code for my unit test on GitHub.

Benefits of Unit Testing

Although my main goal for unit tests with this extension was to ensure I wouldn’t accidentally break something in the future as I made updates, it also uncovered another bug I never would have encountered through manual testing since it didn’t effect the overall functionality.

Before I tracked down the bug, add_discount returned true when I applied a coupon for an existing customer with the “new customer” restriction (broken), but get_applied_coupons returned 0 (working). I was missing an important filter even though the desired functionality still worked in the end! Who knows if this would have caused issues when used in conjunction with other extensions?

With the new unit tests in place, it will be incredibly easy to test the extension against new versions of WooCommerce and WordPress as they are released. It will also help ensure the reliability of the extension if I ever make updates or add new features.

As I mentioned at the top of the post, this is my first experience building out PHP unit tests. If you find any errors or have additional recommendations please share them in the comments!

About Devin

I'm a WordPress developer based in Austin, Texas. I run a little theme shop called DevPress and work for a startup called Nano. Find me on twitter @devinsays.

4 Responses

  1. If you’re writing tests for a specific extension, you’ll want to find a way to hook into the test suite (or write your own that loads the necessary dependencies) without copying the existing tests; as the code base changes so too will the tests, and you don’t want to be chasing down “failing” tests that were written against an older version!

    Disclosure: haven’t looked at Woo’s test suite, so YMMV ;)

    Thanks for writing about this, Devin. Getting started is the hardest part of unit testing, so guides like this are invaluable!

  2. Jon

    nice article but I dispute the statement:
    “There’s also great documentation if you’re setting up unit tests for the first time.”
    In fact the documentation amounts to:

    Simply change to the plugin root directory and type:
    $ phpunit

    If it doesn’t work (which it won’t) there is no information which would assist to try to resolve the problem and no information about what this is trying to do.

    There’s some resolution here:
    https://wordpress.org/support/topic/woocommerce-phpunit-test-instructions-fail/

    Essentially if using the woocommerce/tests/bin/install.sh
    you also need to get eg:
    svn co https://develop.svn.wordpress.org/trunk/ wordpress-develop

    and copy over the data and includes directories from there to the wordpress-tests-lib directory that the woocommerce test installer created.

Leave a Reply