1. Go to this page and download the library: Download alvarodelera/pest-wp-plugin library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
alvarodelera / pest-wp-plugin example snippets
// tests/Integration/MyPluginTest.php
use function PestWP\createPost;
use function PestWP\createUser;
it('can create posts', function () {
$post = createPost(['post_title' => 'Hello World']);
expect($post)->toBeInstanceOf(WP_Post::class)
->and($post->post_title)->toBe('Hello World');
});
it('can test user permissions', function () {
$editor = createUser('editor');
loginAs($editor);
expect(current_user_can('edit_posts'))->toBeTrue()
->and(current_user_can('manage_options'))->toBeFalse();
});
// tests/Pest.php
use PestWP\Concerns\InteractsWithDatabase;
// Enable database isolation for integration tests
uses(InteractsWithDatabase::class)->in('Integration');
// tests/Pest.php
use PestWP\Config;
use PestWP\Database\TransactionManager;
/*
|--------------------------------------------------------------------------
| Load Your Plugin
|--------------------------------------------------------------------------
|
| Tell PestWP to load your plugin when WordPress initializes.
| Use the absolute path to your main plugin file.
|
*/
Config::plugins([
dirname(__DIR__) . '/my-plugin.php',
]);
/*
|--------------------------------------------------------------------------
| Database Isolation
|--------------------------------------------------------------------------
*/
uses()
->beforeEach(fn () => TransactionManager::beginTransaction())
->afterEach(fn () => TransactionManager::rollback())
->in('Integration');
// tests/Pest.php
use PestWP\Config;
use PestWP\Database\TransactionManager;
/*
|--------------------------------------------------------------------------
| Plugin Configuration
|--------------------------------------------------------------------------
*/
// Load your main plugin
Config::plugins(dirname(__DIR__) . '/my-awesome-plugin.php');
// Or load multiple plugins if your plugin has dependencies
Config::plugins([
dirname(__DIR__) . '/vendor/woocommerce/woocommerce.php', // Dependency
dirname(__DIR__) . '/my-woo-extension.php', // Your plugin
]);
// Load MU-plugins (loaded before regular plugins)
Config::muPlugins([
dirname(__DIR__) . '/tests/mu-plugins/test-helpers.php',
]);
// Set active theme (optional)
Config::theme('twentytwentyfour');
/*
|--------------------------------------------------------------------------
| Custom Setup Hooks
|--------------------------------------------------------------------------
*/
// Run code BEFORE WordPress loads (define constants, etc.)
Config::beforeWordPress(function () {
define('MY_PLUGIN_DEBUG', true);
define('MY_PLUGIN_TEST_MODE', true);
});
// Run code AFTER WordPress loads (set options, add filters, etc.)
Config::afterWordPress(function () {
// Set plugin options for testing
update_option('my_plugin_api_key', 'test-api-key');
update_option('my_plugin_settings', [
'feature_x' => true,
'feature_y' => false,
]);
// Add test-specific filters
add_filter('my_plugin_external_api', fn() => 'mock-response');
});
/*
|--------------------------------------------------------------------------
| Database Isolation
|--------------------------------------------------------------------------
*/
uses()
->beforeEach(fn () => TransactionManager::beginTransaction())
->afterEach(fn () => TransactionManager::rollback())
->in('Integration');
// tests/Integration/MyPluginTest.php
use function PestWP\createPost;
use function PestWP\createUser;
describe('My Plugin', function () {
it('registers custom post type on init', function () {
// Your plugin should have registered this via init hook
expect(post_type_exists('my_custom_type'))->toBeTrue();
});
it('adds custom meta box', function () {
do_action('add_meta_boxes', 'post');
global $wp_meta_boxes;
expect($wp_meta_boxes['post']['normal']['default'])
->toHaveKey('my_plugin_meta_box');
});
it('filters the content', function () {
$post = createPost(['post_content' => 'Original content']);
$filtered = apply_filters('the_content', $post->post_content);
expect($filtered)->toContain('Modified by my plugin');
});
it('
use function PestWP\createPost;
use function PestWP\createUser;
it('can create and retrieve a post', function () {
$post = createPost([
'post_title' => 'My Test Post',
'post_content' => 'Test content here',
]);
expect($post)
->toBeInstanceOf(WP_Post::class)
->and($post->post_title)->toBe('My Test Post');
});
it('can create users with different roles', function () {
$editor = createUser('editor');
expect($editor->roles)->toContain('editor');
});
// tests/Integration/MyPluginTest.php
it('registers custom post type', function () {
do_action('init');
expect(post_type_exists('my_cpt'))->toBeTrue();
});
it('filters content correctly', function () {
$result = apply_filters('the_content', 'Hello');
expect($result)->toContain('Modified by my plugin');
});
// tests/Browser/AdminTest.php
it('can create post from admin', function () {
$config = browser();
// Login first
visit($config['base_url'] . loginUrl())
->type('user_login', $config['admin_user'])
->type('user_pass', $config['admin_password'])
->press('Log In')
->assertPathBeginsWith('/wp-admin');
// Create post
visit($config['base_url'] . newPostUrl())
->type(postTitleSelector(), 'My Post')
->click(publishButtonSelector())
->wait(1)
->click(publishButtonSelector())
->assertSee('Post published');
});
use function PestWP\createPost;
// With default values
$post = createPost();
// With custom values
$post = createPost([
'post_title' => 'Custom Title',
'post_content' => 'Custom content',
'post_status' => 'draft',
'post_type' => 'page',
]);
// IDE autocomplete works!
expect($post->ID)->toBeInt();
expect($post->post_title)->toBe('Custom Title');
use function PestWP\createUser;
// Quick role creation
$editor = createUser('editor');
// With additional arguments
$admin = createUser('administrator', [
'display_name' => 'Test Admin',
]);
// Full custom arguments
$user = createUser([
'user_login' => 'testuser',
'user_email' => '[email protected]',
'role' => 'author',
]);
use function PestWP\createTerm;
// Create a category
$categoryId = createTerm('Technology');
// Create in custom taxonomy
register_taxonomy('project_type', 'project');
$termId = createTerm('Web Development', 'project_type');
// With additional arguments
$termId = createTerm('Featured', 'category', [
'description' => 'Featured content',
'slug' => 'featured',
]);
use function PestWP\createAttachment;
// Auto-generates a dummy image
$attachmentId = createAttachment();
// With parent post
$post = createPost();
$attachmentId = createAttachment('', $post->ID);
// With custom arguments
$attachmentId = createAttachment('', 0, [
'post_title' => 'My Image',
'post_excerpt' => 'Image caption',
]);
use function PestWP\loginAs;
use function PestWP\createUser;
it('can test editor permissions', function () {
$editor = createUser('editor');
loginAs($editor);
expect(current_user_can('edit_posts'))->toBeTrue()
->and(current_user_can('manage_options'))->toBeFalse();
});
// Can also login by user ID
loginAs($userId);
use function PestWP\logout;
it('can logout current user', function () {
$user = createUser();
loginAs($user);
expect(isUserLoggedIn())->toBeTrue();
logout();
expect(isUserLoggedIn())->toBeFalse();
});
use function PestWP\currentUser;
use function PestWP\isUserLoggedIn;
it('can get current user', function () {
$admin = createUser('administrator');
loginAs($admin);
$current = currentUser();
expect($current->ID)->toBe($admin->ID)
->and($current->roles)->toContain('administrator');
});
it('can check if user is logged in', function () {
expect(isUserLoggedIn())->toBeFalse();
loginAs(createUser());
expect(isUserLoggedIn())->toBeTrue();
});
use function PestWP\loginAs;
use function PestWP\createUser;
it('can test subscriber permissions', function () {
loginAs(createUser('subscriber'));
expect(current_user_can('read'))->toBeTrue()
->and(current_user_can('edit_posts'))->toBeFalse();
});
it('can test administrator permissions', function () {
loginAs(createUser('administrator'));
expect(current_user_can('manage_options'))->toBeTrue()
->and(current_user_can('delete_users'))->toBeTrue();
});
it('can switch between users', function () {
$editor = createUser('editor');
$author = createUser('author');
loginAs($editor);
expect(current_user_can('edit_others_posts'))->toBeTrue();
loginAs($author);
expect(current_user_can('edit_others_posts'))->toBeFalse();
});
use function PestWP\createPost;
it('can check post statuses', function () {
$published = createPost(['post_status' => 'publish']);
$draft = createPost(['post_status' => 'draft']);
expect($published)->toBePublished();
expect($draft)->toBeDraft();
});
it('can test WP_Error objects', function () {
$error = new WP_Error('invalid_data', 'Invalid input');
expect($error)
->toBeWPError()
->toHaveErrorCode('invalid_data');
});
use function PestWP\createPost;
use function PestWP\createUser;
it('can check post metadata', function () {
$post = createPost();
update_post_meta($post->ID, 'price', 99);
expect($post)
->toHaveMeta('price', 99)
->toHaveMetaKey('price');
});
it('can check user metadata', function () {
$user = createUser();
update_user_meta($user->ID, 'favorite_color', 'blue');
expect($user)
->toHaveUserMeta('favorite_color', 'blue')
->toHaveMetaKey('favorite_color');
});
it('test A creates a post', function () {
$post = createPost(['post_title' => 'Test Post']);
expect(get_post($post->ID))->not->toBeNull();
});
it('test B does not see the post from test A', function () {
// The post from test A doesn't exist here
$posts = get_posts(['post_title' => 'Test Post']);
expect($posts)->toBeEmpty();
});
// tests/Browser/PostsTest.php
use function PestWP\Functions\getBrowserConfig;
it('can log into WordPress dashboard', function () {
$config = getBrowserConfig();
visit($config['base_url'] . '/wp-login.php')
->type('user_login', $config['admin_user'])
->type('user_pass', $config['admin_password'])
->press('Log In')
->assertPathBeginsWith('/wp-admin')
->assertSee('Dashboard');
});
it('can create a new post', function () {
$config = getBrowserConfig();
visit($config['base_url'] . '/wp-admin/post-new.php')
->type('[aria-label="Add title"]', 'My New Post')
->press('Publish')
->wait(1)
->press('Publish') // Confirm
->assertSee('Post published');
});
// In tests/Pest.php (created by pest-setup-browser)
function browser(): array
{
return [
'base_url' => 'http://localhost:8080',
'admin_user' => 'admin',
'admin_password' => 'password',
];
}
use function PestWP\Functions\adminUrl;
use function PestWP\Functions\loginUrl;
use function PestWP\Functions\newPostUrl;
use function PestWP\Functions\editPostUrl;
use function PestWP\Functions\postsListUrl;
// Build admin URLs
loginUrl(); // http://localhost:8080/wp-login.php
adminUrl(); // http://localhost:8080/wp-admin/
adminUrl('edit.php'); // http://localhost:8080/wp-admin/edit.php
adminUrl('my-plugin'); // http://localhost:8080/wp-admin/admin.php?page=my-plugin
// Post URLs
newPostUrl(); // New post
newPostUrl('page'); // New page
editPostUrl(123); // Edit post ID 123
postsListUrl('post', 'draft'); // List drafts
use function PestWP\Functions\postTitleSelector;
use function PestWP\Functions\publishButtonSelector;
use function PestWP\Functions\blockSelector;
use function PestWP\Functions\editorNoticeSelector;
it('can create a post using locators', function () {
$config = browser();
visit($config['base_url'] . newPostUrl())
->wait(1) // Wait for Gutenberg to load
->type(postTitleSelector(), 'My Post')
->click(publishButtonSelector())
->wait(1)
->click(publishButtonSelector())
->assertSee('Post published');
});
// Target specific blocks
blockSelector('core/paragraph'); // [data-type='core/paragraph']
blockSelector('core/heading'); // [data-type='core/heading']
use function PestWP\Functions\menuSelector;
use function PestWP\Functions\noticeSelector;
use function PestWP\Functions\tableRowSelector;
use function PestWP\Functions\buttonSelector;
// Menu navigation
menuSelector('Posts'); // Admin menu item
submenuSelector('Settings', 'General'); // Submenu item
// Notices
noticeSelector('success'); // Success notices
noticeSelector('error'); // Error notices
// Data tables
tableRowSelector('My Post'); // Row by title
rowActionSelector('edit'); // Row action link
use PestWP\Concerns\InteractsWithDatabase;
// Enable database isolation for integration tests
uses(InteractsWithDatabase::class)->in('Integration');
// Ensure you have the trait in tests/Pest.php
uses(PestWP\Concerns\InteractsWithDatabase::class)->in('Integration');