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 = 'firstname.lastname@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|
|// 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!