Testing Guide

Comprehensive testing setup including PHPUnit tests, PHPStan static analysis, and CI/CD integration for robust development.

Home / Documentation / Testing Guide

Testing Overview

Scriptlog uses two complementary testing approaches:

Tool Purpose Coverage
PHPUnit Unit and integration testing Functional correctness
PHPStan Static code analysis Type safety, code quality

Test Suite Metrics

Snapshot from the last recorded run (August 2026) — rerun the suite to refresh these totals. Source of truth: docs/dev-docs/TESTING_GUIDE.md.

Metric Value
Total Tests 2,269
Test Files 167
Assertions 8,074
Skipped / Risky 15 skipped, 60 risky
PHPUnit Version 9.6.35
Target Coverage 40%
Current Coverage ~38%

PHPUnit binary lives at lib/vendor/bin/phpunit (there is no vendor/bin/phpunit). The dev environment runs PHP 8.5 with PHPUnit 9.6, which emits deprecation noise; suppress for readable output: php -d error_reporting='E_ALL' lib/vendor/bin/phpunit.

Test Categories

Category Description
Unit Tests Utility function tests, class existence tests
Integration Tests Database CRUD operations using blogware_test database
Service Tests Business-logic tests for lib/service/ classes
API Tests REST API tests under tests/api/, run via the dedicated API Tests suite

phpunit.xml defines 5 suites: Scriptlog Test Suite, Unit Tests, Integration Tests, Service Tests, API Tests.

Test Coverage Plan

The test coverage plan is organized into phases. All phases are complete; the table below is a historical record of when tests were added (see docs/dev-docs/TESTING_GUIDE.md §3):

Phase Status

Phase Priority Status New Tests Cumulative Total
Pre-existing — Complete 833 833
Phase 1: DAO Integration HIGH Complete 92 925
Phase 2: Service Layer HIGH Complete 148 1,073
Phase 3: Core Classes MEDIUM Complete 65 1,138
Phase 4: Controllers MEDIUM Complete 34 1,172
Phase 5: Utilities LOW Complete 68 1,240
Password Protected Posts HIGH Complete 59 —

Total suite: 2,269 tests across 167 files, 8,074 assertions (August 2026 snapshot).

PHPUnit Testing

Running Tests

BASH PHPUnit Commands
# Run all tests (uses phpunit.xml: 5 test suites)
lib/vendor/bin/phpunit

# PHP 8.5 dev environment emits deprecation noise; suppress for readable output:
php -d error_reporting='E_ALL' lib/vendor/bin/phpunit

# Or via Composer
composer test

# Run with coverage (requires Xdebug)
lib/vendor/bin/phpunit --coverage-html coverage

# Run specific test file
lib/vendor/bin/phpunit tests/unit/ImageDisplayTest.php

# Run tests matching pattern
lib/vendor/bin/phpunit --filter "EmailValidation"

# PHP 7.4 compatibility gate (enforced by pre-commit hook)
composer compat

Test Database Setup

Tests use a separate database (blogware_test) to avoid affecting production data. The test database uses unprefixed table names; production uses a randomly generated prefix stored under db.prefix in the config.

BASH commands
# Create test database
php tests/setup_test_db.php

# Or manually
mysql -u root -p -e "CREATE DATABASE blogware_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"

PHPStan Static Analysis

PHPStan is a static analysis tool that finds bugs in your code without running it.

Configuration Files

File Purpose
phpstan.neon Main configuration
phpstan.baseline.neon Baseline of known issues to ignore

PHPStan Configuration

NEON phpstan.neon
includes:
    - phpstan.baseline.neon

parameters:
    phpVersion: 70400

    paths:
        - lib/
        - index.php

    excludePaths:
        - lib/vendor/*
        - lib/core/HTMLPurifier/*
        - lib/core/ServiceException.php
        - tests/*

    bootstrapFiles:
        - tests/phpstan-bootstrap.php

    reportUnmatchedIgnoredErrors: false

    level: 0

Running PHPStan

BASH PHPStan Commands
# Run static analysis (level 1 via composer)
composer phpstan

# Run strict analysis (level 5)
composer phpstan:strict

# Run with memory limit (recommended)
lib/vendor/bin/phpstan analyse --memory-limit=1G

# Run with specific config
lib/vendor/bin/phpstan analyse --configuration=phpstan.neon

# Generate/update baseline
lib/vendor/bin/phpstan analyse --generate-baseline=phpstan.baseline.neon

# Increase analysis level for stricter checks
lib/vendor/bin/phpstan analyse -l 5

Key Settings

  • phpVersion: Set to 70400 for PHP 7.4 compatibility
  • level: Base config is level 0 (most lenient). The composer phpstan script runs at level 1; composer phpstan:strict runs at level 5 — increase gradually for stricter checks
  • excludePaths: Excludes vendor and third-party code (HTMLPurifier) plus the test suite itself
  • bootstrapFiles: Loads tests/phpstan-bootstrap.php before analysis

Shell (BATS) Tests

Shell utilities are covered with BATS (requires bats on PATH):

BASH BATS Commands
# Unit tests for siege-tui.sh (sandboxed HOME per test)
bats tests/bats

# Include the opt-in end-to-end test (spawns a local php -S fixture on a free port)
SIEGE_TUI_E2E=1 bats tests/bats

Writing Tests

PHPUnit Test Structure

PHP ExampleTest.php
<?php
use PHPUnit\Framework\TestCase;

class MyTest extends TestCase
{
    public function testSomething(): void
    {
        $this->assertTrue(true);
        $this->assertEquals(1, 1);
        $this->assertIsString('test');
    }
    
    public function testWithFunction(): void
    {
        if (function_exists('some_function')) {
            $result = some_function('input');
            $this->assertIsString($result);
        }
    }
}

Best Practices

Single Responsibility

Test one thing per method. Each test should verify a single, specific behavior.

Descriptive Names

Method names should clearly describe what is being tested. Use test[Behavior] pattern.

Arrange-Act-Assert

Structure tests with clear setup (Arrange), action (Act), and verification (Assert) phases.

Mock External Dependencies

Use mocks for database, filesystem, and external services to isolate unit tests.

PHPStan Best Practices

  • Fix errors incrementally — start with level 0, then increase gradually
  • Update baseline regularly — run with --generate-baseline after significant changes
  • Add type hints — improves both PHPStan analysis and code readability
  • Document exceptions — use @throws PHPDoc tags for exceptions

Recently Added Tests

Latest test additions to the project (April 2026) — see docs/dev-docs/TESTING_GUIDE.md for the full file list.

April 2026

Medoo & Membership Tests

tests/unit/MedooinFunctionsTest.php 26 tests

Unit tests for is_medoo_database(), is_db_database(), db_build_where(), medoo_select(), medoo_insert(), medoo_update(), medoo_delete() (PDO::FETCH_ASSOC compatibility)

tests/integration/MedooinIntegrationTest.php 8+ tests

Integration tests for database selection and operations, including table prefix handling

tests/unit/MembershipFunctionsTest.php 26 tests

Tests for is_registration_unable(), membership_default_role(), membership_get_role(), registration role and user level mappings

tests/integration/MembershipIntegrationTest.php 8 tests

Integration tests for membership settings and role configuration

April 2026

PostDao Security Tests

tests/unit/PostDaoSecurityTest.php 6 tests

Security-focused tests for the PostDao class: onlyPublished defaults to true in findPosts()/findPost(), author parameter handling, post_status = 'publish' and post_visibility = 'public' filters

SQL injection prevention verification
Security filters validation
ORDER BY column whitelist testing
April 2026

Password-Protected Posts — 59 tests across 3 files

tests/unit/ProtectedPostTest.php 12 tests

Tests for protect_post(), encrypt_post(), decrypt_post(), checking_post_password(), grab_post_protected(), visibility validation (public, private, protected)

tests/unit/ProtectedPostRateLimitTest.php 20 tests

Rate limiting (5 attempts per 15 minutes), old-attempt expiry, per-post/IP limits, password strength validation, session-based unlock storage

tests/unit/PostControllerProtectedPostTest.php 27 tests

Visibility and password validation, encryption/decryption flow, session handling, form validation errors, CSRF protection, required field validation

CI/CD Integration

GitHub Actions Workflows

CI runs without any database or MySQL service. PHP unit tests and static analysis run in .github/workflows/tests.yml (on main for push/PR, plus manual dispatch), and end-to-end tests run via Playwright in .github/workflows/playwright.yml:

YAML .github/workflows/tests.yml
name: Tests

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  workflow_dispatch:

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.1'
          extensions: pdo, pdo_mysql, mbstring, json, curl, gd, fileinfo, openssl
          tools: composer:v2

      - name: Install dependencies
        run: composer install --no-interaction --prefer-dist
        env:
          COMPOSER_PLATFORM_CHECK: 'false'

      - name: Run Unit Tests (no database required)
        run: lib/vendor/bin/phpunit --configuration phpunit.xml --testsuite "Unit Tests" --no-coverage --colors=never

      - name: Run PHPStan
        run: composer phpstan

End-to-end browser tests live in .github/workflows/playwright.yml (npx playwright test). Database-backed Integration/Service suites are local-only — run php tests/setup_test_db.php first, then lib/vendor/bin/phpunit.

Pre-commit Hook

The repository ships a pre-commit hook at .githooks/pre-commit. Enable it after clone:

BASH enable hook
git config core.hooksPath .githooks

The hook enforces:

  • No .sql files or unexpected non-class files under lib/ (SQL belongs in install/)
  • PHP 7.4 backward compatibility via the PHPCompatibility sniff (phpcs-compatibility.xml) on every staged PHP file — it blocks commits that use PHP 8.0+ features or rely on changed defaults (e.g. html_entity_decode() flags changed in PHP 8.1)

Do not bypass with git commit --no-verify. Run composer compat on your files before committing.

Troubleshooting

PHPUnit Issues

Issue Solution
Tests fail with "Database not found" Run php tests/setup_test_db.php, or create the blogware_test database manually
Integration tests skip unexpectedly Ensure Registry::set('dbc', ...) is called in setUpBeforeClass for DAO-dependent tests
DAO locale/lang_code too long Keep test locale values ≤ 10 chars for VARCHAR(10) columns
PHPUnit deprecation noise on PHP 8.5 Run with php -d error_reporting='E_ALL' lib/vendor/bin/phpunit
Xdebug required for coverage Install Xdebug or skip coverage with --no-coverage

PHPStan Issues

Issue Solution
Memory limit exceeded Run with --memory-limit=1G
Too many errors Use baseline or increase level gradually
False positives Add to ignoreErrors in phpstan.neon
Missing bleedingEdge.neon Remove from includes in phpstan.neon

Additional Resources

Official documentation and guides for testing tools.