Our test case class should extend the DokanTestCase class.
- Isolate the SUT: Ensure that your tests are isolated and only test the SUT (System under test) without external dependencies.
- Use Meaningful Test Names: Use descriptive names for your test methods to indicate what they are testing.
- Test Edge Cases: Include test cases for edge conditions and potential error scenarios.
- Maintain Readability: Write clean and readable test code. Use comments if necessary to explain complex logic.
- Keep Tests Independent: Ensure that each test is independent of others and does not rely on any shared state.
A well-organized directory structure is crucial for maintaining clarity and manageability. Here’s an example structure:
plugin-dir/
│
├── tests/
│ ├── php/
│ │ ├── src/
│ │ │ ├── Unit/
│ │ │ │ └── ClassTest.php
│ │ │ ├── Integration/
│ │ │ │ └── ClassIntegrationTest.php
│ │ │ └── TestCase.php
│ │ └── bootstrap.php
│ │
│ └── pw/
│ └── Files and folders for the Playwright tests
│
└── ... Other files
We can follow the same file path of the source. For example, the test case file for includes/Services/MyService.php could be tests/php/src/Unit/MyServiceTest.php or tests/php/src/Integration/MyServiceTest.php.
- Use unit tests for core logic, utility functions, and isolated classes.
- Aim to cover as much of your code as possible with unit tests.
- Run unit tests frequently during development to catch issues early.
- Use integration tests for verifying interactions between components.
- Test scenarios that involve multiple units working together.
- Run integration tests to ensure that different parts of your system work well together, especially after major changes or before releases.
- Balance: Maintain a good balance between unit tests and integration tests. Both are crucial for comprehensive test coverage.
- Mocking: Use mocking frameworks (e.g., Mockery, Brain Monkey) to isolate components during unit testing.
- Environment: Set up a controlled environment for integration testing to avoid side effects and ensure repeatability.
To write unit tests in Dokan smoothly, your test class should extend the WeDevs\Dokan\Test\DokanTestCase abstract class. For example
<?php
namespace WeDevs\Dokan\Test\Unit;
use WeDevs\Dokan\Test\DokanTestCase;
class SampleTest extends DokanTestCase {
protected $is_unit_test = true; // For unit test.
public function test_sample_method() {
$this->assertTrue( true );
}
}If
$is_unit_testistruethenDokanTestCasewill not create utility for the API and Database.
There are two utility methods named get_request and post_request in the DokanTestCase class.
<?php
namespace WeDevs\Dokan\Test\Integration;
use WeDevs\Dokan\Test\DokanTestCase;
class SampleTest extends DokanTestCase {
// Ensure route is registered.
public function test_ensure_my_route_is_registered()
{
$routes = $this->server->get_routes( $this->namespace );
$full_route = $this->get_route( 'my-route' );
// Assert route is registered.
$this->assertArrayHasKey( $full_route, $routes );
// Assert route is registered only for GET method.
$this->assertNestedContains( [ 'methods' => [ 'GET' => true ] ], $routes[ $full_route ] );
}
// GET & POST request test.
public function test_sample_method() {
// GET request.
$response = $this->get_request( 'route', $filter_params );
$data = $response->get_data();
$status_code = $response->get_status();
// POST request.
$response = $this->post_request( 'route', $request_params );
$data = $response->get_data();
$status_code = $response->get_status();
// PUT request
$response = $this->put_request( 'route', $update_params );
$data = $response->get_data();
$status_code = $response->get_status();
// DELETE request
$response = $this->delete_request( 'route', $delete_params );
$data = $response->get_data();
$status_code = $response->get_status();
}
}$filter_params and $request_params are the array of key => value.
You should extend the DokanAjaxTestCase to write the the ajax test.
<?php
namespace WeDevs\Dokan\Test\Integration;
use WeDevs\Dokan\Test\DokanAjaxTestCase;
use WPAjaxDieContinueException;
class SampleTest extends DokanAjaxTestCase {
public function test_sample_method() {
$user = $this->factory()->seller->create();
// Set the current user to the created seller
wp_set_current_user( $user );
// Set up POST variables
$_POST['nonce'] = wp_create_nonce( 'dokan_withdraw_make_default' );
$_POST['action'] = 'dokan_withdraw_handle_make_default_method';
$_POST['method'] = 'paypal';
// Handle the AJAX request
try {
$this->_handleAjax( 'dokan_withdraw_handle_make_default_method' );
} catch ( WPAjaxDieContinueException $e ) {
// Catch the die() statement in AJAX handling
$this->fail( $e->getMessage() );
}
wp_set_current_user( $user );
// Get the last response
$response = $this->_last_response;
if ( empty( $response ) ) {
$this->fail( 'No response from AJAX handler' );
}
// Decode the JSON response
$data = json_decode( $response, true );
if ( json_last_error() !== JSON_ERROR_NONE ) {
$this->fail( 'Invalid JSON response: ' . json_last_error_msg() );
}
}
}Unit tests should be independent of external sources such as databases, APIs, etc. They must always return the expected result. Set the $is_unit_test property to true when writing unit tests by extending the DokanTestCase class.
<?php
namespace WeDevs\Dokan\Test\Unit; // Unit test namespace.
use WeDevs\Dokan\Test\DokanTestCase;
class SampleTest extends DokanTestCase {
/**
* Indicates if the test is a unit test.
*
* @var bool
*/
protected $is_unit_test = true;
public function test_sample_method() {
// Your test cases.
}
}Grouping test cases in PHPUnit allows you to organize and execute specific sets of tests conveniently. You can use the @group annotation to tag methods or entire classes with a group name. This makes it easier to run only the relevant test cases without executing the entire test suite.
In our case, We may group our test cases by
module-name.
To group test cases, simply add the @group your-group-name annotation above your test method or class. Here's an example:
<?php
namespace WeDevs\Dokan\Test\Unit; // Unit test namespace.
use WeDevs\Dokan\Test\DokanTestCase;
/**
* @group my-module
*/
class SampleTest extends DokanTestCase {
/**
* Indicates if the test is a unit test.
*
* @var bool
*/
protected $is_unit_test = true;
public function test_sample_method() {
// Your test cases.
}
}In this example, the SampleTest class is grouped under the my-module group.
To run test cases for a specific group, use the following command in your terminal:
./vendor/bin/phpunit --group my-moduleThis command will execute all the test cases tagged with the my-module group.
For more detailed information on using groups in PHPUnit, refer to the official documentation:
This approach helps you organize your tests more efficiently, especially in larger projects where running the entire test suite might be time-consuming.
Since WeDevs\Dokan\Test\DokanTestCase class composes the PHPUnit, Mockery and Brain Monkey so all methods of these packages are available in your test class.
- PHPUnit for general assertion.
- Brain Monkey for WordPress assertion.
WeDevs\Dokan\Test\DokanTestCase also use the DBAssertionTrait which contains the following methods to assert the database records.
assertDatabaseHas( string $table, array $data = [] ): Assert that a table contains at least one row matching the specified criteria.assertDatabaseCount( string $table, int $count, array $data = [] ): Assert that a table contains the specified number of rows matching the criteria.
$product_id = $this->factory()->product->create(
[
'status' =>'publish'
]
);
$this->assertDatabaseHas( 'posts', [ 'status' => 'publish' ] );
$this->assertDatabaseCount( 'posts', 1, [ 'ID' => $product_id ] );We may use assertNestedContains if It is required to assert value to a nested Array.
$array = [
'key1' => [
'subkey1' => 'value1',
'subkey2' => 'value2'
],
'key2' => 'value3'
];
// Use the custom assertion method
$this->assertNestedContains( [ 'subkey1' => 'value1' ], $array );
$this->assertNestedContains( [ 'key2' => 'value3' ], $array );