Testing Guide
Comprehensive testing setup including PHPUnit tests, PHPStan static analysis, and CI/CD integration for robust development.
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
# 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.
# 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
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
# 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
70400for PHP 7.4 compatibility - level: Base config is level 0 (most lenient). The
composer phpstanscript runs at level 1;composer phpstan:strictruns 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.phpbefore analysis
Shell (BATS) Tests
Shell utilities are covered with BATS (requires bats on PATH):
# 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
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-baselineafter significant changes - Add type hints — improves both PHPStan analysis and code readability
- Document exceptions — use
@throwsPHPDoc 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.
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
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
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:
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:
git config core.hooksPath .githooks
The hook enforces:
- No
.sqlfiles or unexpected non-class files underlib/(SQL belongs ininstall/) - 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.