Version: 1.3.0 | Last Updated: August 2026
- Overview
- PHPUnit Testing
- Test Coverage Plan
- Static Analysis with PHPStan
- Running Tests
- Test Database Setup
- Writing Tests
- CI/CD Integration
This project uses two complementary testing approaches:
| Tool | Purpose | Coverage |
|---|---|---|
| PHPUnit | Unit and integration testing | Functional correctness |
| PHPStan | Static code analysis | Type safety, code quality |
| 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% |
| 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 |
| Category | Files |
|---|---|
Core classes (lib/core, excl. HTMLPurifier) |
103 |
| DAO classes | 19 |
| Service classes | 23 |
| Controller classes (incl. 12 API) | 32 |
| Handler classes (incl. admin commands) | 48 |
| Model classes | 9 |
| Validator classes | 5 |
DTO classes (incl. dto/api/) |
5 |
| Utility function files | 224 |
Add deep coverage for all DAO methods.
| Area | Tests |
|---|---|
| UserDao | 15 |
| PostDao | 15 |
| TopicDao | 10 |
| CommentDao | 10 |
| MediaDao | 10 |
| PageDao | 8 |
| MenuDao | 6 |
| PluginDao | 6 |
| ThemeDao | 6 |
| ConfigurationDao | 6 |
| Phase 1 Total | 92 |
Test business logic in Service classes.
| Area | Tests |
|---|---|
| UserService | 15 |
| PostService | 12 |
| TopicService | 8 |
| CommentService | 10 |
| MediaService | 8 |
| ConfigurationService | 6 |
| ThemeService | 6 |
| PluginService | 6 |
| DownloadService | 4 |
| NotificationService | 13 |
| Phase 2 Total | 88 |
Test critical core functionality.
| Area | Tests |
|---|---|
| Authentication | 10 |
| SessionMaker | 8 |
| FormValidator | 15 |
| Paginator | 8 |
| Sanitize | 8 |
| DbFactory | 4 |
| Dispatcher | 6 |
| View | 6 |
| Phase 3 Total | 65 |
Test HTTP request handling.
| Area | Tests |
|---|---|
| PostController | 8 |
| UserController | 8 |
| CommentController | 6 |
| MediaController | 6 |
| TopicController | 6 |
| Phase 4 Total | 34 |
Fill gaps in utility function testing.
| Area | Tests |
|---|---|
| Upload utilities | 8 |
| Security utilities | 6 |
| Email utilities | 4 |
| Session utilities | 4 |
| Cache utilities | 4 |
| Phase 5 Total | 26 |
| 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 | 466 |
Total New Tests Added: 466 Total Suite: 2,269 tests across 167 files, 8,074 assertions
The phase table above is a historical record of when tests were added. Current suite totals as of August 2026: 2,269 tests across 167 files with 8,074 assertions (15 skipped, 60 risky).
-
β
tests/unit/MedooinFunctionsTest.php(26 tests)- Tests for
is_medoo_database(),is_db_database(),db_build_where() - Tests for
medoo_select(),medoo_insert(),medoo_update(),medoo_delete() - Tests for PDO::FETCH_ASSOC return format compatibility
- Tests for
-
β
tests/integration/MedooinIntegrationTest.php(8+ tests)- Integration tests for database selection and operations
- Tests for table prefix handling
-
β
tests/unit/MembershipFunctionsTest.php(26 tests)- Tests for
is_registration_unable(),membership_default_role() - Tests for
membership_get_role(),membership_get_role_name() - Tests for registration role and user level mappings
- Tests for
-
β
tests/integration/MembershipIntegrationTest.php(8 tests)- Integration tests for membership settings and role configuration
- β
tests/unit/PostDaoSecurityTest.php(6 tests)- Verifies onlyPublished parameter defaults to true in findPosts()
- Verifies onlyPublished parameter defaults to true in findPost()
- Verifies author parameter is properly defined
- Verifies ORDER BY column whitelist prevents SQL injection
- Verifies status filter: post_status = 'publish'
- Verifies visibility filter: post_visibility = 'public'
- β
tests/integration/UserDaoIntegrationTest.php - β
tests/integration/PostDaoIntegrationTest.php(Includes performance/eager loading tests) - β
tests/integration/PostDaoMethodIntegrationTest.php - β
tests/integration/TopicDaoTest.php - β
tests/integration/PageDaoIntegrationTest.php - β
tests/integration/MenuDaoIntegrationTest.php - β
tests/integration/PluginDaoIntegrationTest.php - β
tests/integration/ThemeDaoIntegrationTest.php
Total: 59 tests across 3 files
-
β
tests/unit/ProtectedPostTest.php(12 tests)- Tests for
protect_post(),encrypt_post(),decrypt_post() - Tests for
checking_post_password(),grab_post_protected() - Visibility validation tests (public, private, protected)
- Tests for
-
β
tests/unit/ProtectedPostRateLimitTest.php(20 tests)- Rate limiting logic tests (5 attempts limit per 15 minutes)
- Old attempts expiration tests
- Separate limits per post ID and IP
- Password strength validation tests (length, uppercase, lowercase, number, special char)
- Session-based unlock storage tests
- Tests for:
is_unlock_rate_limited(),track_failed_unlock_attempt(),clear_failed_unlock_attempts(),get_failed_unlock_attempts(),check_post_password_strength()
-
β
tests/unit/PostControllerProtectedPostTest.php(27 tests)- Visibility validation tests (public, private, protected)
- Password validation for protected posts
- Content encryption/decryption flow
- Session handling for protected posts
- Form validation error handling
- CSRF protection tests
- Required field validation tests
- β
tests/service/UserServiceTest.php(18 tests) - β
tests/service/PostServiceTest.php(24 tests) - β
tests/service/TopicServiceTest.php(7 tests) - β
tests/service/CommentServiceTest.php(10 tests) - β
tests/service/MediaServiceTest.php(16 tests) - β
tests/service/ConfigurationServiceTest.php(10 tests) - β
tests/service/ThemeServiceTest.php(10 tests) - β
tests/service/PluginServiceTest.php(13 tests) - β
tests/service/MenuServiceTest.php(14 tests) - β
tests/service/PageServiceTest.php(16 tests) - β
tests/service/NotificationServiceTest.php(14 tests) - β
tests/service/DataRequestServiceTest.php - β
tests/service/ScheduledPostServiceTest.php - β
tests/service/DownloadCreateLinkTest.php
Core tests live in tests/core/ and tests/unit/. The dedicated SessionMaker, Paginator, Sanitize, DbFactory, and View test files were later removed during test consolidation; those classes are still exercised indirectly by integration and service tests.
- β
tests/core/AuthenticationTest.php(10 tests - user access control, cookie handling, login) - β
tests/core/FormValidatorTest.php(15 tests - input validation, sanitization, JSON output) - β
tests/unit/DispatcherDispatchTest.php(6 tests - URL routing, content validation, 404 handling)
The original tests/controller/* files were consolidated into tests/unit/ and tests/api/unit/. Current controller test files:
- β
tests/unit/PostControllerProtectedPostTest.php(27 tests) - β
tests/unit/PostControllerValidationTest.php - β
tests/unit/SearchControllerTest.php - β
tests/unit/ApiControllerTest.php - β
tests/unit/QueryApiControllerTest.php - β
tests/unit/SearchApiControllerTest.php - β
tests/api/unit/*ApiControllerTest.php(10 API endpoint controller tests)
- β
tests/unit/AdminLocaleInitializationTest.php - β
tests/unit/ApiHateoasTest.php - β
tests/unit/ApiResponseTest.php - β
tests/unit/AppKeyTest.php - β
tests/unit/BootstrapTest.php - β
tests/unit/ConfigFileGenerationTest.php - β
tests/unit/DownloadPageDataTest.php - β
tests/unit/DownloadServiceTest.php - β
tests/unit/DownloadSettingsTest.php - β
tests/unit/DownloadUtilityTest.php - β
tests/unit/FrontServiceTest.php - β
tests/unit/GenerateOpenApiSpecTest.php - β
tests/unit/GenerateRequestTest.php - β
tests/unit/HandlerRegistryTest.php - β
tests/unit/handlers/HandlerStructureTest.php - β
tests/unit/handlers/PostHandlerTest.php - β
tests/unit/I18nManagerTest.php - β
tests/unit/ImageDisplayTest.php - β
tests/unit/ImportUtilitiesTest.php - β
tests/unit/InstallationTest.php - β
tests/unit/LanguageSwitcherTest.php - β
tests/unit/LocaleDetectorTest.php - β
tests/unit/LocaleRouterTest.php - β
tests/unit/MedooinFunctionsTest.php(26 tests) - β
tests/unit/MembershipFunctionsTest.php(26 tests) - β
tests/unit/NavigationI18nTest.php - β
tests/unit/OpenApiSpecVerificationTest.php - β
tests/unit/PageCacheTest.php - β
tests/unit/PerformanceOptimizationTest.php - β
tests/unit/PostControllerProtectedPostTest.php(27 tests) - β
tests/unit/PostControllerValidationTest.php - β
tests/unit/PostDaoSecurityTest.php - β
tests/unit/PostDaoUpdateFixTest.php - β
tests/unit/ProtectedPostRateLimitTest.php(20 tests) - β
tests/unit/ProtectedPostTest.php(12 tests) - β
tests/unit/RateLimiterTest.php - β
tests/unit/ScriptlogCryptonizeTest.php - β
tests/unit/SidebarNavigationTest.php - β
tests/unit/ThemeI18nTest.php - β
tests/unit/ThemeRendererTest.php - β
tests/unit/ThemeUploadTest.php - β
tests/unit/TranslationLoaderTest.php
Additional unit tests live under tests/unit/handlers/, tests/unit/validator/, and tests/unit/dto/. API tests are under tests/api/unit/ and tests/api/integration/ (plus tests/api/unit/dto/), and a smoke test under tests/smoke/.
PHPStan is a static analysis tool that finds bugs in your code without running it.
| File | Purpose |
|---|---|
phpstan.neon |
Main configuration |
phpstan.baseline.neon |
Baseline of known issues to ignore |
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- phpVersion: Set to
70400for PHP 7.4 compatibility - level: Currently at level 0 (most lenient). The
composer phpstanscript runs at level 1;composer phpstan:strictruns at level 5. - excludePaths: Excludes vendor and third-party code (HTMLPurifier) plus the test suite itself
- bootstrapFiles: Loads
tests/phpstan-bootstrap.phpbefore analysis
# 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/EmailValidationTest.php
# Run tests matching pattern
lib/vendor/bin/phpunit --filter "EmailValidation"# 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 5Create a script to run both:
#!/bin/bash
echo "Running PHPUnit tests..."
lib/vendor/bin/phpunit
echo ""
echo "Running PHPStan static analysis..."
lib/vendor/bin/phpstan analyse --memory-limit=1G# 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"Tests use a separate database (blogware_test) to avoid affecting production data.
<?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);
}
}
}- Test one thing per method - Each test should verify a single behavior
- Use descriptive names - Method names should describe what is being tested
- Arrange-Act-Assert - Structure tests with clear setup, action, and verification phases
- Mock external dependencies - Use mocks for database, filesystem, etc.
- 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
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: composer install --no-interaction --no-dev
- name: Run PHPUnit
run: lib/vendor/bin/phpunit
- name: Run PHPStan
run: lib/vendor/bin/phpstan analyse --memory-limit=1GThe repository ships a pre-commit hook at .githooks/pre-commit. Enable it after clone:
git config core.hooksPath .githooksThe hook enforces:
- No
.sqlfiles or unexpected non-directory 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.
| 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 |
| Xdebug required for coverage | Install Xdebug or skip coverage with --no-coverage |
| 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 |
| 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 |
Last Updated: August 2026 | Version 1.3.0