9.2 KiB
9.2 KiB
Testing Patterns
Analysis Date: 2026-05-28
Test Framework
Runner:
- PHPUnit
^11.5.3(installed via Composer) - Config:
src/phpunit.xml
Assertion Library:
- PHPUnit native assertions (
$this->assertTrue(),$this->assertSame(),$this->assertNull(),$this->assertGuest()) - Laravel test response assertions (
$response->assertStatus(),$response->assertOk(),$response->assertSessionHasNoErrors(),$response->assertRedirect(),$response->assertSessionHasErrorsIn())
Mocking:
- Mockery
^1.6available as dependency - No mock usage detected in existing tests
Run Commands:
composer test # Runs: php artisan config:clear --ansi && php artisan test
./vendor/bin/phpunit # Direct PHPUnit invocation
phpunit.xml Configuration
File: src/phpunit.xml
Key settings:
colors="true"— colored outputbootstrap="vendor/autoload.php"- Test suites:
Unit(tests/Unit) andFeature(tests/Feature) - Source inclusion:
appdirectory - Environment overrides:
APP_ENV=testingDB_CONNECTION=sqliteDB_DATABASE=:memory:— in-memory SQLite for testsCACHE_STORE=arraySESSION_DRIVER=arrayMAIL_MAILER=arrayQUEUE_CONNECTION=syncBCRYPT_ROUNDS=4— faster password hashingBROADCAST_CONNECTION=nullPULSE_ENABLED=false,TELESCOPE_ENABLED=false,NIGHTWATCH_ENABLED=false
Test File Organization
Location:
- Feature tests:
src/tests/Feature/ - Unit tests:
src/tests/Unit/
Naming:
- PascalCase class names matching the tested resource:
ExampleTest,ProfileTest,AuthenticationTest - Method names: snake_case descriptive names:
test_the_application_returns_a_successful_response,test_profile_page_is_displayed - Method prefix:
test_prefix convention
Directory Structure:
tests/
├── Feature/
│ ├── Auth/
│ │ ├── AuthenticationTest.php (empty)
│ │ ├── EmailVerificationTest.php (empty)
│ │ ├── PasswordConfirmationTest.php (empty)
│ │ ├── PasswordResetTest.php (empty)
│ │ ├── PasswordUpdateTest.php (empty)
│ │ └── RegistrationTest.php (empty)
│ ├── ExampleTest.php
│ └── ProfileTest.php
├── Unit/
│ └── ExampleTest.php
└── TestCase.php
Base class: src/tests/TestCase.php
namespace Tests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
//
}
- Extends
Illuminate\Foundation\Testing\TestCase - Empty — no custom setup or helper methods defined
- All Feature tests extend
Tests\TestCase
Test Structure
Suite Organization:
Feature tests follow standard Laravel testing patterns:
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ProfileTest extends TestCase
{
use RefreshDatabase;
public function test_profile_page_is_displayed(): void
{
$user = User::factory()->create();
$response = $this
->actingAs($user)
->get('/profile');
$response->assertOk();
}
}
Unit tests use plain PHPUnit:
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
class ExampleTest extends TestCase
{
public function test_that_true_is_true(): void
{
$this->assertTrue(true);
}
}
Patterns:
- Arrange-Act-Assert structure clearly visible
- Variable names:
$user,$response - Method return type
: voidconsistently used - Chained HTTP methods:
$this->actingAs($user)->patch('/profile', [...])->assertSessionHasNoErrors()
Database Testing
Approach:
- In-memory SQLite (
DB_DATABASE=:memory:) for all tests RefreshDatabasetrait used inProfileTestto migrate schema before each testUser::factory()->create()for creating test users
ProfileTest database patterns:
public function test_profile_information_can_be_updated(): void
{
$user = User::factory()->create();
$response = $this
->actingAs($user)
->patch('/profile', [
'name' => 'Test User',
'email' => 'test@example.com',
]);
$response
->assertSessionHasNoErrors()
->assertRedirect('/profile');
$user->refresh();
$this->assertSame('Test User', $user->name);
$this->assertSame('test@example.com', $user->email);
$this->assertNull($user->email_verified_at);
}
public function test_user_can_delete_their_account(): void
{
$user = User::factory()->create();
$response = $this
->actingAs($user)
->delete('/profile', ['password' => 'password']);
$response
->assertSessionHasNoErrors()
->assertRedirect('/');
$this->assertGuest();
$this->assertNull($user->fresh());
}
Key patterns:
$user->refresh()to reload model from database after update$user->fresh()to check if record still exists (null after delete)$this->assertGuest()to verify logout->assertSessionHasErrorsIn('userDeletion', 'password')for error bag validation
Fixtures and Factories
Factory location: src/database/factories/UserFactory.php
UserFactory:
class UserFactory extends Factory
{
protected static ?string $password;
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
];
}
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}
- Uses
fake()helper for Faker data generation - Static
$passwordcached to hash only once unverified()state method for email-unverified users- No factories exist for
station,rainfall,waterlevel,siren, ornotificationtables
Coverage
Requirements:
- No coverage target configured in
phpunit.xml - No coverage report configuration present
- No CI pipeline detected enforcing coverage thresholds
View Coverage:
./vendor/bin/phpunit --coverage-html coverage/
# or with --coverage-text for terminal output
Test Types
Unit Tests:
- 1 file:
tests/Unit/ExampleTest.php - Single trivial assertion:
$this->assertTrue(true) - Extends
PHPUnit\Framework\TestCasedirectly (no Laravel app boot) - No real unit tests for services, models, or utilities exist
Feature Tests:
- 2 populated files + 6 empty files:
ExampleTest.php— tests/returns 200ProfileTest.php— 5 tests covering profile page, update, email verification, delete, and wrong-password deletiontests/Feature/Auth/*Test.php— 6 files, all empty (zero bytes)
E2E Tests:
- Not used. No Laravel Dusk or Playwright detected.
Test Coverage Gaps
Critical gaps:
- No controller tests:
RainfallController,WaterLevelController,AdminController,SirenController,NotificationController,MapController,cctvController,LocaleController— zero tests - No API tests:
Api/StationController,Api/AuthController,Api/AlertController— zero tests - No service tests:
FcmService— zero tests - No export tests:
HourlyRainfallExport,WaterLevelExport— zero tests - No middleware tests:
AdminMiddleware,LocalizationMiddleware— zero tests - No model tests:
Usermodel features (casts, fillable, hidden, password reset notification) — zero tests - 6 placeholder test files in
tests/Feature/Auth/are empty (created by Laravel Breeze scaffold but never populated)
Common Patterns
Async Testing:
- Not applicable — Laravel is synchronous; no async patterns in tests
Error Testing:
public function test_correct_password_must_be_provided_to_delete_account(): void
{
$user = User::factory()->create();
$response = $this
->actingAs($user)
->from('/profile')
->delete('/profile', [
'password' => 'wrong-password',
]);
$response
->assertSessionHasErrorsIn('userDeletion', 'password')
->assertRedirect('/profile');
$this->assertNotNull($user->fresh());
}
Patterns:
->from('/profile')to set previous URL for redirect-back testsassertSessionHasErrorsIn('userDeletion', 'password')for error bag validation->assertRedirect('/profile')to check redirect destination$this->assertNotNull($user->fresh())to verify model was NOT deleted
Authentication Testing:
$user = User::factory()->create();
$response = $this
->actingAs($user)
->get('/profile');
$response->assertOk();
Session Assertions:
assertSessionHasNoErrors()— no validation errorsassertSessionHasErrorsIn('userDeletion', 'password')— specific error in error bag- Session flash checked via redirect assertion
CI Configuration
- No CI pipeline configuration detected (no
.github/workflows/,.gitlab-ci.yml,Jenkinsfile, etc.) - No code quality checks enforced in CI
- No automated test running pipeline detected
Testing analysis: 2026-05-28