Skip to content

Testing Guide ​

Last updated: 2026-10-12

Comprehensive testing documentation for Filament Address Pro package.

Table of Contents ​


Prerequisites ​

The test suite runs from the package directory, not from inside your Laravel application. When you install the package via Composer, the dev dependencies (Pest, Testbench, Mockery) are not installed. You need to install them first.

Step 1 — Install dev dependencies ​

bash
cd vendor/viewflex/filament-address-pro
composer install

Step 2 — Configure the test database connection ​

bash
cp phpunit.xml.dist phpunit.xml
# Edit phpunit.xml and set DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD

Step 3 — Create and seed the test database ​

Integration, Feature, Smoke, and Performance tests require a pre-seeded MySQL database. Unit tests do not — skip this step if you only want to run Unit tests.

bash
mysql -u root -e "CREATE DATABASE filament_addresses_test;"
vendor/bin/testbench migrate --force
vendor/bin/testbench db:seed --force \
  --class="Viewflex\FilamentAddress\Database\Seeders\CountriesDomainSeeder"

Why MySQL? The seeded country data (256 countries, 17K+ subdivisions) makes SQLite in-memory impractically slow. MySQL with pre-seeded data and DatabaseTransactions rollback is ~315x faster.

Running unit tests only (no database needed) ​

bash
cd vendor/viewflex/filament-address-pro
composer install
vendor/bin/pest --testsuite=Unit

Overview ​

The Filament Address Pro package has 1,308 comprehensive tests across 5 categories:

  • Pure Unit Tests (252 tests): No Laravel dependencies, instant feedback
  • Integration Tests (868 tests): Laravel + Database with transactions - includes authorization and pre-release validation
  • Feature Tests (72 tests): Full-stack HTTP integration
  • Smoke Tests (73 tests): Quick health checks (passing + a few skipped for API keys)
  • Performance Tests (37 tests): Timing benchmarks and baselines

Current Status: 1,308 tests passing, 12 skipped (expected)

Total Test Execution Time: ~3 minutes (175 seconds)


Quick Start ​

bash
# Run all tests
composer test
# or: vendor/bin/pest

# Run specific test suite
vendor/bin/pest --testsuite=Unit
vendor/bin/pest --testsuite=Integration
vendor/bin/pest --testsuite=Feature
vendor/bin/pest --testsuite=Smoke
vendor/bin/pest --testsuite=Performance

# Run specific groups
vendor/bin/pest --group=smoke
vendor/bin/pest --group=performance
vendor/bin/pest --group=pre-release

# Run specific test file
vendor/bin/pest tests/Unit/Extractors/JapaneseAddressExtractorTest.php

# Run with coverage (requires Xdebug)
vendor/bin/pest --coverage

Test Architecture ​

Directory Structure ​

tests/
├── Unit/                    # Pure unit tests (no Laravel)
│   ├── Extractors/         # Address component extractors
│   ├── Services/           # Service layer logic
│   └── Utilities/          # Helper utilities
├── Integration/            # Laravel + Database tests
│   ├── Models/             # Model behavior
│   ├── Services/           # Service integration
│   └── Configuration/      # Config validation
├── Feature/                # Full-stack HTTP tests
│   └── Services/           # API integration
├── Smoke/                  # Quick health checks
│   ├── PackageHealthTest.php
│   ├── CountryFixturesSmokeTest.php
│   ├── ExtractorsSmokeTest.php
│   ├── ServiceHealthSmokeTest.php
│   └── DataIntegritySmokeTest.php
├── Performance/            # Performance benchmarks
│   ├── GeocodingPerformanceTest.php
│   ├── SubdivisionMatchingPerformanceTest.php
│   ├── ExtractorPerformanceTest.php
│   └── FixtureParsingPerformanceTest.php
└── Support/                # Test utilities
    └── TestAddressParser.php

Three-Tier Testing Strategy ​

  1. Pure Unit Tests - Fast, isolated, no dependencies

    • Instant feedback (< 0.2s)
    • Test business logic in isolation
    • No database, no HTTP, no external services
  2. Integration Tests - Laravel features + Database

    • Fast with database transactions (< 5s)
    • Test Laravel integration (models, services, events)
    • Use filament_addresses_test MySQL database
  3. Feature Tests - Full stack integration

    • External API calls (geocoding, verification)
    • End-to-end workflows
    • Slower but comprehensive

Test Categories ​

1. Pure Unit Tests (112 tests) ​

Location: tests/Unit/Speed: ~0.14 seconds Database: None Purpose: Test business logic in isolation

What's Tested:

  • Address component extractors (Default, Japanese, Korean, ConfigBased)
  • Postal code validation
  • Address formatting logic
  • Cache key generation
  • Utility functions

Example:

php
test('japanese extractor identifies address numbers correctly', function () {
    $extractor = new JapaneseAddressExtractor();

    expect($extractor->isAddressNumber('4-chōme-2-8'))->toBeTrue()
        ->and($extractor->isAddressNumber('Tokyo Tower'))->toBeFalse();
});

Run: vendor/bin/pest --testsuite=Unit


2. Integration Tests (521 tests) ​

Location: tests/Integration/Speed: ~3-5 seconds Database: filament_addresses_test (MySQL) Purpose: Test Laravel integration with database

What's Tested:

  • Model relationships and scopes
  • Service layer with database
  • Configuration consistency
  • Observers and events
  • Database queries and performance
  • Subdivision matching
  • Quality scoring

Key Features:

  • Uses database transactions (fast rollback)
  • Pre-seeded test database (315x faster than SQLite)
  • Tests real Laravel features

Example:

php
test('country has many subdivisions relationship', function () {
    $us = Country::where('country_code', 'US')->first();
    $subdivisions = $us->activeSubdivisions()->get();

    expect($subdivisions)->toBeInstanceOf(Collection::class)
        ->and($subdivisions->count())->toBeGreaterThan(50);
});

Run: vendor/bin/pest --testsuite=Integration


3. Feature Tests (52 tests) ​

Location: tests/Feature/Speed: Variable (depends on API calls) Database: Test database Purpose: Test full-stack workflows with external services

What's Tested:

  • International address testing (19 tests) - 46 countries covered
  • Geocoding service (15 tests)
  • Address verification (USPS, Google)
  • Complete address processing workflows
  • Import/export functionality

Example:

php
test('geocoding service returns valid coordinates', function () {
    $service = app(GeocodingService::class);
    $result = $service->geocode('1600 Pennsylvania Avenue NW, Washington, DC');

    expect($result)->not->toBeNull()
        ->and($result['lat'])->toBeCloseTo(38.8977, 0.01)
        ->and($result['lng'])->toBeCloseTo(-77.0365, 0.01);
})->group('api');

Run: vendor/bin/pest --testsuite=Feature


4. Smoke Tests (71 tests) ​

Location: tests/Smoke/Speed: ~7.5 seconds Purpose: Quick health checks for critical functionality

Current Status: Passing (a few skipped when API keys not configured - expected)

What's Tested:

  • Package health and configuration
  • Country fixtures integrity
  • Extractor functionality
  • Service health and instantiation
  • Data integrity checks
  • Component health
  • API connectivity (skipped without API keys)

Categories:

  • Core Health (smoke): Database, seeding, formatting
  • API Connectivity (smoke-api): External services (skipped without API keys)
  • Components (smoke-components): Filament components and service health

When to Run:

  • Before every release
  • Daily in CI/CD
  • After database migrations
  • After configuration changes
  • When debugging package setup

Example:

php
test('all 46 country fixture files exist', function () {
    $parser = new TestAddressParser();
    $countries = $parser->getAllCountries();

    expect(count($countries))->toBeGreaterThanOrEqual(46);
})->group('smoke', 'smoke-fixtures');

Run:

bash
# All smoke tests
vendor/bin/pest --group=smoke

# Specific categories
vendor/bin/pest --group=smoke-fixtures
vendor/bin/pest --group=smoke-extractors
vendor/bin/pest --group=smoke-services
vendor/bin/pest --group=smoke-data

# Exclude API tests (for CI)
vendor/bin/pest --group=smoke --exclude-group=smoke-api

Documentation: tests/Smoke/README.md


5. Pre-Release Validation (5 tests) ​

Location: tests/Integration/PreReleaseCountryValidationTest.php (part of Integration suite) Speed: Included in Integration tests (~3 minutes total) Purpose: Ensure all 256 countries work before release

What's Tested:

  • All 256 countries can geocode without errors (5 comprehensive tests)
  • 245 countries successful, 11 appropriately skipped (uninhabited territories)
  • 154 countries can extract subdivisions
  • 245 countries can process full workflow
  • Database properly seeded
  • Capital addresses available

Results:

  • ✅ All 256 countries validated successfully
  • ✅ 0 failures
  • ✅ Comprehensive assertions for each country

When to Run:

  • Before every package release
  • After major configuration changes
  • When adding new countries

Run:

bash
vendor/bin/pest --group=pre-release

These tests validate the package against all 256 countries to ensure zero critical failures before release.


Running Tests ​

By Test Suite ​

bash
# Pure unit tests (fastest)
vendor/bin/pest --testsuite=Unit

# Integration tests
vendor/bin/pest --testsuite=Integration

# Feature tests (may hit APIs)
vendor/bin/pest --testsuite=Feature

# Smoke tests (health checks)
vendor/bin/pest --testsuite=Smoke

# Performance benchmarks
vendor/bin/pest --testsuite=Performance

By Group ​

bash
# Smoke tests
vendor/bin/pest --group=smoke

# Performance tests
vendor/bin/pest --group=performance

# Pre-release validation
vendor/bin/pest --group=pre-release

# International addresses
vendor/bin/pest --group=international

# API-dependent tests (skip in CI)
vendor/bin/pest --exclude-group=api

By File ​

bash
# Specific test file
vendor/bin/pest tests/Unit/Extractors/JapaneseAddressExtractorTest.php

# Specific test method
vendor/bin/pest --filter="japanese extractor identifies address numbers"

Daily Development Workflow ​

bash
# Fast feedback loop (< 5s)
vendor/bin/pest --testsuite=Unit --testsuite=Integration

# Before commit (< 15s)
vendor/bin/pest --exclude-group=api,pre-release

# Before push (< 2 min)
vendor/bin/pest --group=smoke,performance

# Before release (< 5 min)
composer test

Test Coverage ​

Current Coverage ​

Total: 1062 tests passing, 10 skipped

CategoryTestsSpeedStatus
Pure Unit252~0.14sAll passing
Integration868~3-5sAll passing, 4 skipped (includes pre-release)
Feature72VariableAll passing (46 countries)
Smoke73~7.5sPassing, 6 skipped (API keys)
Performance37~0.74sAll passing, 2 skipped (expected)
Total1,308~4.5min100% Success

Coverage by Feature ​

  • ✅ Address Models: 100% covered
  • ✅ Country/Subdivision Models: 100% covered
  • ✅ Geocoding Service: 100% covered
  • ✅ Address Verification: 100% covered
  • ✅ Bulk Operations: 100% covered
  • ✅ Quality Scoring: 100% covered
  • ✅ Import/Export: 100% covered
  • ✅ Extractors: 100% covered (Default, Japanese, Korean, ConfigBased)
  • ✅ International Support: 46 countries with fixture tests
  • ✅ All Countries: 256 countries validated for basic functionality

Writing Tests ​

Local Setup for Contributors ​

Tests use Orchestra\Testbench and run standalone from the package directory. One-time setup:

bash
# 1. Copy the distribution config and add your local DB credentials
cp phpunit.xml.dist phpunit.xml
# Edit phpunit.xml: set DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD

# 2. Create and seed the test database
mysql -u root -e "CREATE DATABASE filament_addresses_test;"
vendor/bin/testbench migrate --force
vendor/bin/testbench db:seed --force \
  --class="Viewflex\FilamentAddress\Database\Seeders\CountriesDomainSeeder"

# 3. Run tests
composer test
# or: vendor/bin/pest

The phpunit.xml file is gitignored; only phpunit.xml.dist is committed.

Test Structure ​

All tests use Pest PHP syntax:

php
use Viewflex\FilamentAddress\Tests\TestCase;

uses(TestCase::class);

test('description of what is being tested', function () {
    // Arrange
    $input = 'test data';

    // Act
    $result = someFunction($input);

    // Assert
    expect($result)->toBe('expected output');
})->group('category');

Unit Test Example ​

php
test('postal code validation works correctly', function () {
    expect(CountryService::validatePostalCode('\d{5}', '12345'))->toBeTrue()
        ->and(CountryService::validatePostalCode('\d{5}', 'ABC'))->toBeFalse();
})->group('unit');

Integration Test Example ​

php
test('country has active subdivisions', function () {
    $country = Country::factory()->create();
    $subdivision = CountrySubdivision::factory()->create([
        'country_id' => $country->id,
        'is_active' => true,
    ]);

    $active = $country->activeSubdivisions()->get();

    expect($active)->toHaveCount(1)
        ->and($active->first()->id)->toBe($subdivision->id);
})->group('integration');

Feature Test Example ​

php
test('address processing completes full workflow', function () {
    $place = [
        'formatted_address' => '1600 Pennsylvania Avenue NW, Washington, DC 20500',
        'lat' => 38.8977,
        'lng' => -77.0365,
        'place_id' => 'test-place-id',
    ];

    $result = AddressProcessingService::processFromPlace($place, 'US');

    expect($result)->toHaveKey('address_line_1')
        ->and($result)->toHaveKey('city')
        ->and($result)->toHaveKey('state')
        ->and($result)->toHaveKey('postal_code');
})->group('feature');

Smoke Test Example ​

php
test('geocoding service can be instantiated', function () {
    $service = app(GeocodingService::class);

    expect($service)->toBeInstanceOf(GeocodingService::class);
})->group('smoke', 'smoke-services');

Performance Test Example ​

php
test('operation is fast', function () {
    $start = microtime(true);

    for ($i = 0; $i < 1000; $i++) {
        // Perform operation
    }

    $duration = microtime(true) - $start;

    expect($duration)->toBeLessThan(0.1,
        "Operation: {$duration}s for 1000 iterations");
})->group('performance');

CI/CD Integration ​

GitHub Actions Example ​

yaml
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: 8.2
          extensions: mbstring, pdo_mysql

      - name: Install Dependencies
        run: composer install

      - name: Setup Test Database
        run: |
          mysql -u root -proot -e "CREATE DATABASE filament_addresses_test;"
          vendor/bin/testbench migrate --force
          vendor/bin/testbench db:seed --force \
            --class="Viewflex\FilamentAddress\Database\Seeders\CountriesDomainSeeder"

      - name: Run Unit Tests
        run: vendor/bin/pest --testsuite=Unit

      - name: Run Integration Tests
        run: vendor/bin/pest --testsuite=Integration

      - name: Run Smoke Tests
        run: vendor/bin/pest --group=smoke --exclude-group=smoke-api

      - name: Run Performance Tests
        run: vendor/bin/pest --group=performance

Pre-Release Checklist ​

bash
# 1. Run full test suite
composer test

# 2. Run pre-release validation
vendor/bin/pest --group=pre-release

# 3. Verify smoke tests pass
vendor/bin/pest --group=smoke

# 4. Check performance baselines
vendor/bin/pest --group=performance

# 5. Validate test database
vendor/bin/testbench migrate:fresh --force
vendor/bin/testbench migrate --force
vendor/bin/testbench db:seed --class="Viewflex\FilamentAddress\Database\Seeders\CountriesDomainSeeder" --force

Troubleshooting ​

Common Issues ​

Issue: Tests fail with "Class not found"

bash
# Solution: Regenerate autoload files
composer dump-autoload

Issue: Database tests fail

bash
# Solution: Recreate test database
vendor/bin/testbench migrate:fresh --force
vendor/bin/testbench migrate --force
vendor/bin/testbench db:seed --class="Viewflex\FilamentAddress\Database\Seeders\CountriesDomainSeeder" --force

Issue: Memory limit exceeded

bash
# Solution: Increase PHP memory limit
php -d memory_limit=512M vendor/bin/pest

Issue: Tests timeout

bash
# Solution: Run specific test suites separately
vendor/bin/pest --testsuite=Unit
vendor/bin/pest --testsuite=Integration

Issue: API tests fail with rate limits

bash
# Solution: Skip API tests or run with delays
vendor/bin/pest --exclude-group=api

Performance Issues ​

If tests are slow:

  1. Run Unit tests separately (instant feedback)
  2. Run only the suite you need (e.g. composer test:unit)
  3. Skip Feature tests during development
  4. Use smoke tests for quick validation

Test Database Setup ​

One-Time Setup ​

bash
# Create test database
mysql -u root -e "CREATE DATABASE filament_addresses_test;"

# Run migrations
vendor/bin/testbench migrate --force

# Seed data
vendor/bin/testbench db:seed --class="Viewflex\FilamentAddress\Database\Seeders\CountriesDomainSeeder" --force

Why MySQL Instead of SQLite? ​

  • 315x faster than SQLite in-memory
  • Supports full MySQL features
  • Matches production environment
  • Better for integration tests

  • Smoke Tests: tests/Smoke/README.md - Quick health checks for package functionality
  • Performance Tests: tests/Performance/README.md - Performance benchmarks and profiling

Best Practices ​

1. Test Pyramid ​

        /\
       /  \     Feature Tests (52) - Fewer, slower, broader
      /    \
     /------\   Integration Tests (521) - Moderate, medium, targeted
    /        \
   /----------\ Pure Unit Tests (112) - Many, fast, focused
  /__________/

2. Write Tests First (TDD) ​

  1. Write failing test
  2. Write minimal code to pass
  3. Refactor

3. Keep Tests Fast ​

  • Use pure unit tests when possible
  • Use database transactions
  • Mock external services
  • Run smoke tests frequently

4. Test Behavior, Not Implementation ​

❌ Bad: Testing internal methods ✅ Good: Testing public API and outcomes

5. Use Descriptive Names ​

php
// Good
test('japanese extractor correctly identifies address numbers')

// Bad
test('test1')

6. One Assertion Per Concept ​

php
// Good
test('postal code validation accepts valid codes', function () {
    expect(CountryService::validatePostalCode('\d{5}', '12345'))->toBeTrue();
});

test('postal code validation rejects invalid codes', function () {
    expect(CountryService::validatePostalCode('\d{5}', 'ABC'))->toBeFalse();
});

Summary ​

The Filament Address Pro package has world-class test coverage with:

  • 1,308 comprehensive tests (1,308 passing, 12 skipped)
  • 100% success rate (all executable tests passing)
  • Fast feedback loops (< 5s for unit tests)
  • Smoke tests for quick health checks
  • 46 countries with fixture-based tests
  • 256 countries validated for basic functionality (pre-release)
  • 100% feature coverage for core functionality
  • Authorization system (62 policy tests + 32 Spatie tests)
  • Bulk operations fully tested (import, export, verification)

The testing infrastructure ensures high quality, prevents regressions, and provides confidence for continuous development and deployment.

Released under a commercial license.