Jak testuję mikroserwisy w Laravel — 243 testy, RabbitMQ i OAuth2

Opisuję strategię testowania dla 5 serwisów PHP: testy jednostkowe, feature testy z prawdziwą bazą, mockowanie RabbitMQ i testowanie flow OAuth2.

Pokrycie testami

Serwis Linie Testy
Users 87.8% 54
SSO 83.7% 25
Blog 80.1% 104
Admin 73.0% 28
Frontend 61.4% 46
Razem ~78% 243

Każdy serwis używa SQLite in-memory (DB_CONNECTION=sqlite, :memory:) do testów — bez potrzeby stawiania MySQL dla CI.

Testy feature — realna baza, nie mocki

Preferuję testy feature z prawdziwą bazą zamiast unit testów z mockami. Przykład testu BlogAPI:

class PostApiTest extends TestCase
{
    use RefreshDatabase;

    public function test_can_get_published_posts(): void
    {
        Post::factory()->count(5)->published()->create();
        Post::factory()->count(3)->draft()->create();

        $response = $this->getJson('/api/v1/public/posts');

        $response->assertOk()
            ->assertJsonCount(5, 'data')
            ->assertJsonStructure([
                'data' => [['id', 'title', 'slug', 'excerpt', 'published_at']],
                'meta' => ['total', 'per_page', 'current_page'],
            ]);
    }
}

Mockowanie RabbitMQ

Testy publisherów RabbitMQ nie powinny wymagać działającego brokera. Używam mock():

public function test_publishes_post_viewed_event(): void
{
    $publisher = $this->mock(AnalyticsEventPublisher::class);
    $publisher->shouldReceive('publishPostViewed')
        ->once()
        ->with(Mockery::type('string'), null);

    $this->get('/post/' . $post->slug);
}

Testowanie konsumera RabbitMQ

Consumer to Artisan Command. Test symuluje odebranie wiadomości przez wywołanie handlera bezpośrednio:

public function test_handles_user_updated_event(): void
{
    $author = Author::factory()->create(['user_id' => 42]);

    $handler = app(UserEventsMessageHandler::class);
    $handler->handle([
        'event' => 'user.updated',
        'user_id' => 42,
        'name' => 'Nowe Imię',
        'email' => 'nowy@email.com',
    ]);

    $this->assertDatabaseHas('authors', [
        'user_id' => 42,
        'name' => 'Nowe Imię',
    ]);
}

Testowanie OAuth2 w SSO

Flow OAuth2 ma wiele kroków. Test end-to-end całego flow:

public function test_authorization_code_flow(): void
{
    $client = Passport::client()->create([...]);
    $user = User::factory()->create();

    // Krok 1: GET /oauth/authorize
    $response = $this->actingAs($user)
        ->get('/oauth/authorize?' . http_build_query([
            'client_id' => $client->id,
            'redirect_uri' => 'https://app.test/callback',
            'response_type' => 'code',
        ]));

    // Krok 2: POST approve
    $approveResponse = $this->actingAs($user)
        ->post('/oauth/authorize', [...]);

    $code = parse_url($approveResponse->headers->get('Location'), PHP_URL_QUERY);
    parse_str($code, $params);

    // Krok 3: Wymiana kodu na token
    $tokenResponse = $this->post('/oauth/token', [
        'grant_type' => 'authorization_code',
        'code' => $params['code'],
        'client_id' => $client->id,
    ]);

    $tokenResponse->assertOk()->assertJsonStructure(['access_token', 'refresh_token']);
}

Comments

No comments yet