Install the Package
Add to composer.json (adjust version if needed):
composer require citizen63000/easy-api-tests
Note: Laravel compatibility is implied via Symfony 6.4, but verify PHP 8.1+ and Laravel 10+.
First Use Case: API Test Scaffold Create a test class extending the package’s base:
// tests/Feature/ExampleApiTest.php
use Citizen63000\EasyApiTests\TestCase;
class ExampleApiTest extends TestCase
{
public function test_get_endpoint_returns_data(): void
{
$response = $this->get('/api/example');
$response->assertStatus(200)
->assertJsonStructure(['data']);
}
}
Key: The TestCase provides Laravel/Symfony hybrid assertions (e.g., assertJsonStructure, assertDatabaseHas).
Where to Look First
vendor/citizen63000/easy-api-tests/src/TestCase.php for available methods.actingAs).API Contract Testing Use the package’s assertions to validate responses against OpenAPI/Swagger specs:
public function test_create_user_matches_schema(): void
{
$response = $this->postJson('/api/users', ['name' => 'John']);
$response->assertStatus(201)
->assertJsonMatchesSchema([
'type' => 'object',
'properties' => [
'id' => ['type' => 'integer'],
'name' => ['type' => 'string']
]
]);
}
Authentication Flows Leverage Laravel’s built-in auth helpers with Symfony’s client:
public function test_protected_route_requires_auth(): void
{
$this->actingAs(User::factory()->create())
->get('/api/protected')
->assertOk();
$this->get('/api/protected')
->assertUnauthorized();
}
Database Transactions
Use Symfony’s DatabaseTransactionTrait (if included) for atomic tests:
use Citizen63000\EasyApiTests\DatabaseTransactionTrait;
class UserTest extends TestCase
{
use DatabaseTransactionTrait;
public function test_user_creation_persists_data(): void
{
$this->postJson('/api/users', ['name' => 'Alice']);
$this->assertDatabaseHas('users', ['name' => 'Alice']);
}
}
Hybrid Assertions: Combine Laravel’s assertDatabaseHas with Symfony’s assertResponseStatus:
$response = $this->postJson('/api/orders', ['product_id' => 1]);
$response->assertCreated()
->assertJsonPath('data.id', 1);
$this->assertDatabaseHas('orders', ['product_id' => 1]);
Mocking External APIs:
Use Symfony’s HttpClient mocking (if supported):
$mockHandler = new MockHandler([
new Response(200, [], json_encode(['key' => 'value']))
]);
$client = new Client(['handler' => $mockHandler]);
$this->app->instance(HttpClient::class, $client);
Feature Flags for Tests:
Enable/disable test suites via Laravel’s config('testing.enabled') or Symfony’s TEST_ENVIRONMENT=true.
Symfony 6.4 Breaking Changes
Request::getClientIp() in favor of getRealClientIp(). Update custom middleware:
// Before (deprecated)
$ip = $request->getClientIp();
// After
$ip = $request->getRealClientIp();
void return types and named arguments:
public function test_something(): void { ... } // Required
Dependency Conflicts
symfony/mailer, conflict with Laravel’s symfony/mailer (v5.x). Resolve via composer.json:
"conflict-resolution": {
"symfony/mailer": "6.4.*"
}
composer why symfony/* to audit conflicts.Test Isolation
DatabaseTransactionTrait or Laravel’s refreshDatabase():
public function setUp(): void
{
parent::setUp();
$this->artisan('migrate:fresh');
}
Symfony vs. Laravel Errors: Symfony 6.4 errors may not trigger Laravel’s exception handler. Use:
php artisan config:clear
php artisan cache:clear
Or enable Symfony’s debug mode:
putenv('SYMFONY_DEBUG=1');
HTTP Client Issues:
If HttpClient fails silently, enable verbose logging:
$client = new Client([
'headers' => ['User-Agent' => 'EasyApiTests'],
'debug' => true, // Enable debug output
]);
Custom Assertions
Extend the TestCase to add domain-specific assertions:
namespace Tests\Feature;
use Citizen63000\EasyApiTests\TestCase;
class CustomAssertionsTestCase extends TestCase
{
protected function assertResponseHasPaginatedData(array $expected): void
{
$this->assertResponseStatus(200);
$this->assertJsonStructure([
'data' => ['*'],
'meta' => ['pagination' => ['total', 'per_page']]
]);
// Add custom logic...
}
}
Test Data Factories Integrate Laravel’s factories with Symfony’s test data builders:
public function test_with_factory_data(): void
{
$user = User::factory()->create();
$this->actingAs($user)
->get('/api/profile')
->assertOk()
->assertJsonPath('data.name', $user->name);
}
Performance Testing
Use Symfony’s Stopwatch to measure test execution:
use Symfony\Component\Stopwatch\Stopwatch;
public function test_performance(): void
{
$stopwatch = new Stopwatch();
$event = $stopwatch->start('api_response_time');
$this->get('/api/heavy-endpoint');
$event->stop();
$this->assertLessThan(1000, $event->getDuration()); // <1s
}
.env.testing is loaded for test-specific configs:
putenv('APP_ENV=testing');
TestCase:
protected function getPackageProviders($app): array
{
return [
\Citizen63000\EasyApiTests\ServiceProvider::class,
// Override specific bindings
];
}
pest --parallel or Symfony’s ParallelTestSuite (if supported) to speed up test suites.laravel-shift/visual-regression for API response snapshots:
$this->assertVisualRegression($response->getContent());
jobs:
test:
steps:
- uses: actions/cache@v3
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
How can I help you explore Laravel packages today?