Testing & Test Database Setup

Overview

Testing is a first-class citizen in the ValPress ecosystem. Built upon the robust Laravel testing suite, ValPress provides a comprehensive environment for unit, feature, and integration testing. Whether you are developing a core feature, a custom plugin, or a specialized theme, the testing framework ensures that your code is reliable, performant, and backward-compatible.

Why It Matters

A complex CMS like ValPress, with its extensive hook system and dynamic plugin architecture, requires rigorous testing to prevent regressions.

  • Stability: Ensures that core updates do not break third-party extensions.
  • Confidence: Allows developers to refactor code with the certainty that existing functionality remains intact.
  • Interoperability: Verifies that multiple plugins and themes can coexist without conflict.
  • Documentation: Tests serve as living documentation, demonstrating how APIs are intended to be used.

How It Works

ValPress leverages PHPUnit and Laravel's artisan test runner. When running tests, the application boots in the testing environment, which uses a dedicated database and configuration.

The Testing Lifecycle

  1. Environment Boot: The phpunit.xml file sets specific environment variables (e.g., APP_ENV=testing, DB_CONNECTION=pgsql).
  2. Configuration Isolation: Caches are cleared, and service providers are registered in a test-safe manner.
  3. Database Preparation: The test database is migrated and seeded (optionally using the RefreshDatabase trait).
  4. Plugin/Theme Discovery: ValPress automatically discovers and includes tests located within plugin and theme directories thanks to specialized PSR-4 mappings in composer.json.

Usage

1. PHPUnit Configuration

The phpunit.xml file in the root directory defines the default testing environment. ValPress ships with a pre-configured PostgreSQL setup:

<!-- Default DB settings in phpunit.xml -->
<env name="DB_CONNECTION" value="pgsql"/>
<env name="DB_HOST" value="127.0.0.1"/>
<env name="DB_PORT" value="5454"/>
<env name="DB_DATABASE" value="valpress_tests_db"/>
<env name="DB_USERNAME" value="postgres"/>
<env name="DB_PASSWORD" value="postgres"/>

To override these locally without modifying the shared phpunit.xml, copy .env.testing.example to .env.testing.

Always run tests via:

composer test
# or
php artisan test

Never run phpunit --no-configuration or point DB_DATABASE in .env at a test run — RefreshDatabase executes migrate:fresh and will erase all data in the connected database.

The Update Center core verify pipeline also requires a dedicated test database configured under Settings → Test Database. Use a separate database name from production. See Updates & Maintenance.

2. Creating the Test Database

Before running tests, you must ensure the test database exists.

PostgreSQL (Default)

Create the database and user as specified in your configuration:

# Example using psql
psql -U postgres -c "CREATE DATABASE valpress_tests_db;"

MySQL/MariaDB

If you prefer MySQL, update your DB_CONNECTION and create the database:

mysql -u root -e "CREATE DATABASE valpress_tests_db;"

SQLite (Recommended for Speed)

SQLite is ideal for fast unit tests. You can use an in-memory database or a file:

# In-memory (fastest)
DB_CONNECTION=sqlite
DB_DATABASE=:memory:

# File-based
DB_CONNECTION=sqlite
DB_DATABASE=database/database.sqlite

Note: Ensure touch database/database.sqlite is run if using file-based SQLite.

3. Preparing the Database Schema

Ensure your test database is up to date with the latest migrations:

php artisan migrate --database=testing

4. Running Tests

ValPress provides several ways to execute your test suite.

Running All Tests

# Using the composer script (clears config automatically)
composer test

# Using artisan directly
php artisan test

Running Specific Tests

# Run a specific file
php artisan test tests/Feature/PostsControllerTest.php

# Filter by test name
php artisan test --filter "test_can_create_post"

Code Examples

A Standard Feature Test

This example demonstrates testing a core post creation endpoint using the RefreshDatabase trait.

namespace Tests\Feature;

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class PostCreationTest extends TestCase
{
    use RefreshDatabase;

    /** @test */
    public function an_authorized_user_can_create_a_post()
    {
        // 1. Arrange: Authenticate a user
        $user = User::factory()->create(['role' => 'admin']);
        $this->actingAs($user);

        // 2. Act: Submit a post request
        $response = $this->post('/admin/posts', [
            'title' => 'My Test Post',
            'content' => 'Content of the test post.',
            'status' => 'publish',
        ]);

        // 3. Assert: Verify the post exists in the database
        $response->assertStatus(302);
        $this->assertDatabaseHas('posts', [
            'title' => 'My Test Post'
        ]);
    }
}

Testing a Plugin Hook

Testing how a plugin interacts with ValPress hooks:

namespace Plugins\HelloWorld\Tests;

use Tests\TestCase;

class HookTest extends TestCase
{
    /** @test */
    public function hello_world_filter_modifies_content()
    {
        $originalContent = "Hello";

        // ValPress uses standard apply_filters
        $filteredContent = apply_filters('the_content', $originalContent);

        $this->assertStringContainsString('World', $filteredContent);
    }
}

Advanced Usage

Testing Plugins and Themes

ValPress supports autoload-dev for plugins and themes. Place your tests in:

  • public/plugins/{plugin-slug}/tests/
  • public/themes/{theme-slug}/tests/

These namespaces are automatically mapped to Plugins\Tests\ and Themes\Tests\.

Mocking External Services

Always mock external APIs or mail services to ensure tests remain fast and deterministic.

public function test_external_api_call()
{
    Http::fake([
        'api.valpress.net/*' => Http::response(['status' => 'ok'], 200),
    ]);

    // ... perform action that calls API
}

Best Practices

  • Use RefreshDatabase: Always use this trait for tests that interact with the database to ensure a clean state for every test method.
  • Prefer SQLite for Unit Tests: Unless you are testing database-specific features (like PostgreSQL JSONB), SQLite is significantly faster.
  • Keep Tests Isolated: One test should not depend on the results of another.
  • Mock Services: Use Mail::fake(), Event::fake(), and Http::fake() to prevent side effects.
  • Document Logic via Tests: Use descriptive test names like test_cannot_delete_published_post_without_permission.

Common Mistakes

  • Running Tests on Production DB: Never use phpunit --no-configuration. ValPress aborts tests when APP_ENV is not testing or when DB_DATABASE does not include test. Always use composer test or php artisan test with a dedicated test database configured in phpunit.xml / .env.testing. Never point the Update Center test database at production.
  • Hardcoding IDs: Avoid hardcoding database IDs; use factories to generate related models dynamically.
  • Ignoring Cache: Tests can sometimes fail because of stale configuration caches. Use php artisan config:clear or the composer test command.
  • Missing Autoloading: Ensure your plugin/theme test directories follow the PSR-4 structure defined in composer.json or they won't be discovered.

Summary

Testing in ValPress is designed to be seamless and powerful. By leveraging Laravel's native tools and extending them to the plugin architecture, ValPress ensures that developers can build high-quality, stable extensions with ease. Setting up a dedicated test database and following best practices will result in a faster, more reliable development workflow.