Skip to main content
WP HealthKit

WordPress Plugin Integration Testing: A Complete Framework

June 14, 202617 min readTutorialsBy Jamie

Table of Contents

Introduction

WordPress plugin integration testing is fundamentally different from unit testing. While unit tests isolate individual functions and methods, integration tests verify how your plugin works within the real WordPress environment—interacting with the database, hooks, filters, and other plugins. This distinction matters because most plugin failures don't occur in isolated code; they happen when components interact with WordPress core, databases, and external APIs.

Many WordPress developers skip integration testing entirely, relying only on unit tests or manual testing. This approach creates blind spots. A function might pass every unit test yet fail catastrophically when WordPress applies filters, database transactions occur, or user roles interact with capabilities. Integration tests catch these real-world scenarios before users do.

WP HealthKit recognizes integration testing as critical infrastructure. Our automated audit system analyzes plugin code to identify integration points that need testing coverage. This guide teaches you how to implement comprehensive integration tests that ensure your WordPress plugins work reliably in production environments.

Understanding Integration Testing

Integration testing occupies the middle ground between unit testing and end-to-end testing. A unit test verifies a single function in isolation. An integration test verifies how multiple components work together. An end-to-end test verifies complete user workflows through the entire system.

Unit Test Example:

public function test_sanitize_user_input() {
    $result = sanitize_text_field('<script>alert("xss")</script>');
    $this->assertFalse(strpos($result, '<script>'));
}

This test only verifies WordPress's sanitize_text_field() function works correctly. It doesn't test how your plugin integrates that sanitization into a form submission, database storage, and subsequent retrieval.

Integration Test Example:

public function test_plugin_saves_and_retrieves_user_data() {
    $user_id = self::factory()->user->create();
    $data = array('name' => 'Test User', 'bio' => 'A test bio');
    
    update_user_meta($user_id, 'my_plugin_bio', $data['bio']);
    $saved_data = get_user_meta($user_id, 'my_plugin_bio', true);
    
    $this->assertEquals($data['bio'], $saved_data);
}

This test verifies your plugin correctly stores and retrieves data through WordPress's metadata system. It tests the integration between your code, WordPress functions, and the database.

Why Integration Tests Matter for WordPress

WordPress plugins operate within a complex ecosystem. Your code doesn't run in isolation. It interacts with:

  • The WordPress database through wpdb and metadata APIs
  • WordPress hooks and filters that modify behavior at runtime
  • User roles and capabilities systems
  • Other active plugins that might conflict
  • External APIs and HTTP requests
  • Custom post types, taxonomies, and rewrite rules
  • Caching systems and transients

Integration tests verify these interactions work correctly. They catch bugs that unit tests miss because they test the actual WordPress environment, not mocked versions of it.

Setting Up Your Integration Test Environment

Proper setup is essential for reliable integration tests. Your test environment must closely resemble production while remaining isolated and fast.

WordPress Test Suite Bootstrap

WordPress provides a test suite framework. Begin with proper bootstrapping:

<?php
// tests/bootstrap.php
$_tests_dir = getenv( 'WP_TESTS_DIR' );

if ( ! $_tests_dir ) {
    $_tests_dir = '/tmp/wordpress-tests-lib';
}

if ( ! file_exists( $_tests_dir . '/includes/functions.php' ) ) {
    echo "Could not find wp-tests-lib\n";
    exit( 1 );
}

require $_tests_dir . '/includes/functions.php';

// Load your plugin
require dirname( __FILE__ ) . '/../my-plugin.php';

require $_tests_dir . '/includes/bootstrap.php';

This bootstrap file ensures WordPress test utilities load before your tests run. The WP_TESTS_DIR environment variable points to your WordPress test library installation.

PHPUnit Configuration

Configure phpunit.xml for your test suite:

<?xml version="1.0"?>
<phpunit
    bootstrap="tests/bootstrap.php"
    backupGlobals="false"
    colors="true"
    convertErrorsToExceptions="true"
    convertNoticesToExceptions="true"
    convertWarningsToExceptions="true"
    processIsolation="false"
    stopOnError="false"
    stopOnFailure="false"
    stopOnIncomplete="false"
    stopOnSkipped="false"
    beStrictAboutTestSize="false"
    beStrictAboutOutputDuringTests="true"
    failOnWarning="true"
    failOnRisky="true"
>
    <testsuites>
        <testsuite name="integration">
            <directory suffix=".php">tests/</directory>
        </testsuite>
    </testsuites>
</phpunit>

Database Isolation

Integration tests need a clean database for each test. WordPress provides the WP_UnitTestCase base class that handles this:

<?php
class MyPluginTest extends WP_UnitTestCase {
    
    public function setUp(): void {
        parent::setUp();
        // Database is automatically rolled back before each test
    }
    
    public function test_plugin_creates_table() {
        global $wpdb;
        
        // Run plugin activation which creates a table
        do_action( 'my_plugin_activation' );
        
        // Verify table exists
        $table_name = $wpdb->prefix . 'my_plugin_data';
        $result = $wpdb->get_results( "SHOW TABLES LIKE '$table_name'" );
        
        $this->assertNotEmpty( $result );
    }
}

WP_UnitTestCase automatically rolls back the database after each test, providing isolation without slow teardown operations.

Database Testing in WordPress

Most plugin bugs involve database interactions. Integration tests must thoroughly test database operations.

Testing Custom Tables

If your plugin creates custom database tables during activation:

public function test_plugin_creates_custom_table_with_correct_schema() {
    global $wpdb;
    
    // Trigger activation
    do_action( 'my_plugin_activate' );
    
    $table_name = $wpdb->prefix . 'my_plugin_entries';
    
    // Verify table exists
    $table_exists = $wpdb->get_var( 
        "SHOW TABLES LIKE '$table_name'" 
    );
    $this->assertEquals( $table_name, $table_exists );
    
    // Verify columns
    $columns = $wpdb->get_results( 
        "DESCRIBE $table_name" 
    );
    $column_names = wp_list_pluck( $columns, 'Field' );
    
    $this->assertContains( 'id', $column_names );
    $this->assertContains( 'user_id', $column_names );
    $this->assertContains( 'created_at', $column_names );
}

This test verifies both that the table exists and that it has the expected columns.

Testing Data Persistence

Verify that data persists correctly through WordPress's metadata APIs:

public function test_plugin_metadata_persists_across_queries() {
    $post_id = self::factory()->post->create();
    $test_data = array( 'key1' => 'value1', 'key2' => 'value2' );
    
    // Store metadata
    foreach ( $test_data as $key => $value ) {
        update_post_meta( $post_id, 'my_plugin_' . $key, $value );
    }
    
    // Create new query (simulating page load)
    $retrieved_post = get_post( $post_id );
    $retrieved_data = array(
        'key1' => get_post_meta( $post_id, 'my_plugin_key1', true ),
        'key2' => get_post_meta( $post_id, 'my_plugin_key2', true ),
    );
    
    $this->assertEquals( $test_data, $retrieved_data );
}

Ensure Plugin Quality with WP HealthKit

Your WordPress plugin integration tests are only as good as your ability to identify which integrations need testing. WP HealthKit's automated audit system analyzes your plugin code to identify untested integration points, database interactions, and critical functionality that require integration test coverage.

Get started: Upload your plugin to WP HealthKit and receive a detailed integration testing audit.

Testing Query Relationships

Test that queries work correctly across related data:

public function test_plugin_query_filters_by_user_correctly() {
    $user1 = self::factory()->user->create();
    $user2 = self::factory()->user->create();
    
    // Create data for both users
    update_user_meta( $user1, 'my_plugin_score', 100 );
    update_user_meta( $user2, 'my_plugin_score', 200 );
    
    // Query for user1's score
    $user1_score = get_user_meta( $user1, 'my_plugin_score', true );
    $user2_score = get_user_meta( $user2, 'my_plugin_score', true );
    
    $this->assertEquals( 100, $user1_score );
    $this->assertEquals( 200, $user2_score );
    $this->assertNotEquals( $user1_score, $user2_score );
}

HTTP Mocking and API Testing

Many WordPress plugins make external HTTP requests. Integration tests must verify these requests work correctly without actually calling external APIs.

Using Nonces in Integration Tests

Nonces protect against CSRF attacks. Your integration tests must verify nonce handling:

public function test_plugin_requires_valid_nonce_for_action() {
    $user = self::factory()->user->create( 
        array( 'role' => 'administrator' ) 
    );
    wp_set_current_user( $user );
    
    // Create a valid nonce
    $nonce = wp_create_nonce( 'my_plugin_action' );
    
    // Test with valid nonce
    $_REQUEST['my_plugin_nonce'] = $nonce;
    $result = apply_filters( 'my_plugin_verify_nonce', 
        wp_verify_nonce( $nonce, 'my_plugin_action' ) 
    );
    
    $this->assertNotFalse( $result );
    
    // Test with invalid nonce
    $_REQUEST['my_plugin_nonce'] = 'invalid_nonce';
    $result = wp_verify_nonce( 'invalid_nonce', 'my_plugin_action' );
    
    $this->assertFalse( $result );
}

Mocking External API Calls

Use the tests_add_filter() function to mock HTTP requests:

public function test_plugin_handles_api_response_correctly() {
    // Mock the remote post response
    add_filter( 'pre_http_request', function( $preempt, $args, $url ) {
        if ( strpos( $url, 'api.example.com' ) !== false ) {
            return array(
                'headers'  => array( 'content-type' => 'application/json' ),
                'body'     => json_encode( array( 'status' => 'success', 'data' => array( 'id' => 1 ) ) ),
                'response' => array( 'code' => 200, 'message' => 'OK' ),
            );
        }
        return $preempt;
    }, 10, 3 );
    
    // Call plugin function that makes API request
    $response = my_plugin_fetch_api_data();
    
    $this->assertIsArray( $response );
    $this->assertEquals( 'success', $response['status'] );
}

Testing Hooks and Filters

WordPress hooks and filters are integration points. Your tests must verify hooks fire correctly and filters modify data appropriately.

Testing Action Hooks

Verify that your plugin fires action hooks at the correct times:

public function test_plugin_fires_action_hook_on_data_save() {
    $hook_fired = false;
    
    add_action( 'my_plugin_data_saved', function() use ( &$hook_fired ) {
        $hook_fired = true;
    });
    
    // Call plugin function that should fire the hook
    my_plugin_save_data( array( 'test' => 'data' ) );
    
    $this->assertTrue( $hook_fired );
}

Testing Filter Hooks

Verify that filters correctly modify data:

public function test_plugin_allows_filtering_output() {
    add_filter( 'my_plugin_output', function( $output ) {
        return strtoupper( $output );
    });
    
    $output = my_plugin_get_output( 'hello world' );
    
    $this->assertEquals( 'HELLO WORLD', $output );
}

Testing Hook Priority

Verify hooks execute in the correct order:

public function test_hooks_execute_in_correct_priority() {
    $execution_order = array();
    
    add_action( 'my_plugin_process', function() use ( &$execution_order ) {
        $execution_order[] = 'first';
    }, 10 );
    
    add_action( 'my_plugin_process', function() use ( &$execution_order ) {
        $execution_order[] = 'second';
    }, 20 );
    
    do_action( 'my_plugin_process' );
    
    $this->assertEquals( array( 'first', 'second' ), $execution_order );
}

End-to-End Plugin Testing

End-to-end tests verify complete workflows from user action through database storage and output rendering.

Testing Admin Form Submission

public function test_admin_form_submission_creates_post_with_metadata() {
    // Set up admin user
    $admin = self::factory()->user->create( 
        array( 'role' => 'administrator' ) 
    );
    wp_set_current_user( $admin );
    
    // Create nonce for form
    $_POST['my_plugin_nonce'] = wp_create_nonce( 'my_plugin_save' );
    $_POST['my_plugin_title'] = 'Test Title';
    $_POST['my_plugin_content'] = 'Test Content';
    
    // Simulate form submission
    do_action( 'my_plugin_save_form' );
    
    // Verify post was created
    $posts = get_posts( array(
        'post_type' => 'post',
        'meta_key'  => 'my_plugin_title',
        'meta_value' => 'Test Title',
    ));
    
    $this->assertCount( 1, $posts );
}

Testing User Interaction Workflows

public function test_user_can_submit_and_retrieve_their_data() {
    // Create subscriber user
    $user = self::factory()->user->create( 
        array( 'role' => 'subscriber' ) 
    );
    wp_set_current_user( $user );
    
    // User submits data
    update_user_meta( $user, 'my_plugin_preference', 'dark_mode' );
    
    // Verify plugin respects the preference
    $preference = get_user_meta( $user, 'my_plugin_preference', true );
    $this->assertEquals( 'dark_mode', $preference );
    
    // Switch to admin to verify admin can see user data
    wp_set_current_user( 
        self::factory()->user->create( 
            array( 'role' => 'administrator' ) 
        ) 
    );
    
    $user_preference = get_user_meta( $user, 'my_plugin_preference', true );
    $this->assertEquals( 'dark_mode', $user_preference );
}

Integration tests verify that WordPress plugin integration testing covers real-world plugin behavior. Combined with unit tests, they provide comprehensive coverage that ensures your plugin works reliably in production.

Integration testing verifies that your plugin works correctly with WordPress and other plugins. Unit tests test individual functions in isolation, but integration tests test how components work together—how your plugin's functions interact with WordPress hooks, how your database code works with wpdb, how your authentication code works with WordPress user roles. Integration tests catch issues that unit tests might miss.

Many plugins have excellent unit tests but fail in real WordPress environments because integration behavior differs from test assumptions. Your code might work perfectly in isolation but break when WordPress initializes, when other plugins load, or when certain configurations are present. Integration testing prevents these real-world failures.

Writing good integration tests requires a WordPress test environment. You run tests against an actual WordPress installation with your plugin activated. Tests exercise complete workflows—admin operations, frontend functionality, database interactions, hook execution. This thorough testing reveals compatibility issues, configuration problems, and edge cases before they reach users.

Frequently Asked Questions

What's the difference between integration and unit tests?

Unit tests verify individual functions in isolation with mocked dependencies. Integration tests verify how multiple components work together with real WordPress functions, database, and hooks. WordPress plugin development requires both: unit tests for logic, integration tests for WordPress interactions.

How do I run integration tests in CI/CD?

Use GitHub Actions or similar CI systems to run tests against a test WordPress installation. Set environment variables like WP_TESTS_DIR and WP_VERSION to point to your test environment. Tools like WP Snapshots or Local provide containerized WordPress for testing.

Can I test plugins that depend on other plugins?

Yes, but load dependent plugins in your bootstrap file before running tests. This is often called "integration testing with dependencies." Just ensure you're testing in isolation—use database rollback between tests to prevent state leakage.

How do I test REST API endpoints?

Use WordPress's rest_get_server() and $server->dispatch() functions to test REST endpoints without making actual HTTP requests. This allows integration testing of your REST API with real WordPress data.

Should I test WordPress core functions?

No—WordPress core functions are tested by WordPress itself. Test your plugin's integration with those functions, not the functions themselves. Mock or test your code that uses WordPress functions.

How do I handle plugin activation and deactivation in tests?

Use do_action( 'activate_plugin/my-plugin.php' ) and do_action( 'deactivate_plugin/my-plugin.php' ) to trigger activation/deactivation hooks. However, WP_UnitTestCase handles database setup, so you typically don't need to test database creation—test the code that creates tables instead.

Test Data and Fixtures

Integration tests require realistic test data. You can't just test with one post and expect to catch problems that only appear with hundreds of posts. You can't test pagination with a handful of items. You can't test performance degradation without realistic data volumes.

WordPress provides testing libraries like WP Test Suite and WP Browser for integration testing. These tools let you set up complete WordPress environments, activate plugins, create test data, and verify functionality. The challenge is creating test data that reflects real-world conditions.

Good integration tests use fixtures—pre-defined test data that's consistent across test runs. A fixture might create 1000 posts in various categories with various metadata. Another fixture might create users with different roles and capabilities. By using consistent fixtures, tests become reproducible and reliable.

Test-Driven Development

Consider adopting test-driven development (TDD) for new features. Write tests before writing code. The tests define expected behavior. Then implement code to pass tests. This approach ensures tests are comprehensive and code is testable.

TDD has several benefits. Tests force you to think about edge cases before coding. Code designed to pass tests is usually cleaner and more modular. Tests document expected behavior clearly. By practicing TDD, you improve code quality and test coverage.

TDD isn't always appropriate. For exploratory work or rapid prototyping, TDD slows development. But for core functionality and security-sensitive code, TDD provides excellent protection against regressions.

Multisite and Subdirectory Testing Configurations

WordPress Multisite introduces complexity that single-site testing doesn't catch. Plugins must work correctly when WordPress is installed in subdirectories, when sites use subdomain networks, and when multiple sites share plugin code while maintaining separate data. Integration tests need to verify behavior across different Multisite configurations. Assumptions about the home URL or wp-content directory break in subdirectory Multisite setups. WP HealthKit's integration testing framework specifically includes Multisite variants to catch these assumptions.

REST API Endpoint Behavior Across Versions

WordPress REST API has evolved significantly across versions, with endpoint behaviors changing and new features being added. Integration tests should validate that custom endpoints work correctly in the minimum supported WordPress version and in the latest version. Testing against only the current stable version misses version-specific bugs that will appear when users on older WordPress versions activate the plugin. Continuous integration can test against multiple supported WordPress versions simultaneously.

Plugin Conflict Testing and Interference Detection

The most common reason for plugin integration failures is conflict with other widely-used plugins. Integration tests should include popular plugins like WooCommerce, Elementor, Yoast SEO, and other commonly-used extensions. Testing a plugin in isolation works fine, but combining it with other plugins often reveals unexpected behavior. Shared hooks, overlapping functionality, and conflicting CSS/JavaScript can create problems that unit tests never catch. Professional teams often maintain a test environment with dozens of common plugins activated to replicate real-world conditions.

Widget and Customizer Compatibility Testing

WordPress widgets and the Customizer interface often interact with plugins in subtle ways. Integration tests should verify that custom widgets work in the Customizer, that live previews function correctly, and that widget settings persist properly. Plugins that register custom Customizer controls must test interaction with theme customization. The Customizer's JavaScript-heavy interface can conflict with plugin JavaScript. Testing across different themes reveals theme-specific incompatibilities that single-theme testing misses.

Admin Interface and Settings Page Integration

Plugins adding settings pages to wp-admin must test across different admin color schemes, different browser widths, and different WordPress versions. The Settings API handles most of the work, but custom JavaScript and styling often breaks in unexpected ways. Admin pages should be tested on mobile device widths to ensure WordPress's responsive design works with custom plugin interfaces. Testing with screen readers verifies accessibility compliance for administrative users.

Hook Execution Order and Priority Conflicts

WordPress hooks execute in priority order, but plugins often assume their callbacks run first when multiple plugins hook the same action. Integration tests should verify correct behavior even when multiple plugins hook the same filter or action. The testing environment can register competing hooks to simulate conflicts that occur in the real world.

Database State Reset Between Tests

Integration tests modifying WordPress data must reset the database between tests to ensure isolation. Each test should start with a clean database state. The WP_UnitTestCase class handles this by rolling back database changes after each test. However, complex tests with nested transactions sometimes leave data behind. Proper tearDown() methods clean up test data explicitly. Some tests require specific baseline data before they run, like test users, posts, or terms.

Conclusion

WordPress plugin integration testing bridges the gap between isolated unit tests and real-world plugin behavior. By testing how your plugin interacts with WordPress databases, hooks, filters, and user roles, you catch bugs before they reach production.

Effective integration testing requires:

  • Proper test environment setup with WordPress test suite
  • Database isolation between tests using WP_UnitTestCase
  • Comprehensive testing of database operations and queries
  • Mocking external HTTP requests while testing integration flow
  • Verification of hooks, filters, and action firing
  • End-to-end workflow testing for critical user interactions

WP HealthKit's automated audit system identifies integration testing gaps in your plugin code. Our analysis engine examines your hooks, database interactions, and critical workflows to recommend which integration tests would provide the highest impact.

Ready to verify your plugin's integration testing coverage? Upload your plugin to WP HealthKit and receive detailed recommendations for integration test scenarios your plugin needs.


Learn more about WordPress plugin quality:

Real-World Reliability

Integration tests reveal issues that unit tests never will. By testing your plugin in real WordPress environments, you ensure it actually works for users. Integration testing is the bridge between isolated code and real-world usage. By making integration testing part of your process, you build genuinely reliable plugins. WP HealthKit helps identify areas where your plugin needs integration testing coverage. By analyzing your code and testing approach, we can recommend which features need end-to-end testing to ensure real-world functionality.

Integration testing is where confident plugins come from. Unit tests verify code, but integration tests verify that your plugin actually works for users. By making integration testing part of your process, you build reliable plugins.

Upload your plugin to WP HealthKit to assess test coverage and receive testing recommendations. Unit tests prove functions work in isolation. Integration tests prove your plugin works for users in real WordPress installations. The gap between these two is where real bugs hide. By implementing integration testing, you catch issues that unit tests never would. This investment in testing builds confidence that your plugin works reliably in the wild. Write integration tests that exercise complete workflows. Don't just test individual functions. Test the entire process from user action to database change to output generation. This realistic testing catches real issues. Integration tests catch real-world bugs. Unit tests verify functions. Both matter.

Ready to audit your plugin?

WP HealthKit checks for all the issues in this article and 40+ more across 49 verification layers.

Comments

WordPress Plugin Integration Testing: A Complete Framework | WP HealthKit