PHP Academy · leksjon

Integrasjons- og funksjonstesting

Test hvordan komponentene fungerer sammen i praksis.

Leksjon 3 av 413 trinn

Integrasjons- og funksjonstesting er en gratis leksjon i PHP Academy på CoddyKit. Dette er leksjon 3 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i PHP Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i PHP Academy inneholder totalt 4 leksjoner.

Utover enhetstesten

Enhetstester beviser at en klasse fungerer isolert, med alt simulert. Men mocks kan lyve – den virkelige databasen, rammeverkets ruter eller HTTP-laget oppfører seg annerledes. Integrasjonstester verifiserer at to eller flere ekte komponenter er koblet sammen; funksjonstester prøver applikasjonen fra ende til annen gjennom sitt offentlige inngangspunkt (en HTTP-forespørsel, en konsollkommando). Denne leksjonen forklarer når de brukes, og viser hvordan begge skrives.

Testpyramiden

En sunn testpakke er formet som en pyramide:

  • Mange enhetstester — raske, isolerte og kjøres hele tiden.
  • Færre integrasjonstester — ekte DB/kø/cache, langsommere.
  • Få funksjons-/E2E-tester — hele stakken, tregest og mest skjøre.

Hvis dette snus (en «iskremkjegle» med hovedsakelig E2E-tester), blir testpakken treg og ustabil. Flytt dekningen nedover i pyramiden når det er mulig.

Integrasjon: Repository mot en ekte database

En integrasjonstest for et repository bruker en faktisk databaseforbindelse – ikke en mock – for å fange opp feil i SQL, skjema og mapping. Bruk en dedikert testdatabase (eller en SQLite-database i minnet som samsvarer tilstrekkelig godt med dialekten) og en ekte PDO.

<?php
use PHPUnit\Framework\TestCase;

final class UserRepositoryTest extends TestCase
{
    private \PDO $pdo;

    protected function setUp(): void
    {
        $this->pdo = new \PDO('sqlite::memory:');
        $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
        $this->pdo->exec('CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT)');
    }

    public function test_persists_and_finds_a_user(): void
    {
        $repo = new UserRepository($this->pdo);
        $id = $repo->create('ada@example.com');
        self::assertSame('ada@example.com', $repo->find($id)->email);
    }
}

Isolasjon: Tilbakerulling av transaksjoner

Integrasjonstester må ikke lekke tilstand mellom hverandre. Det raskeste mønsteret er å pakke hver test inn i en transaksjon og rulle den tilbake i tearDown() – databasen går tilbake til en ren tilstand uten ny seed-data. (Trunkering er reservealternativet når koden som testes gjør commit eller bruker DDL.)

<?php
protected function setUp(): void
{
    $this->pdo = TestDb::connection();
    $this->pdo->beginTransaction();
}

protected function tearDown(): void
{
    $this->pdo->rollBack();   // undo everything this test did
}

Fixtures og factories

Tester trenger data. Unngå skjøre, enorme SQL-dumper; foretrekk små factories som bare oppretter radene en test trenger, med fornuftige standardverdier som kan overstyres for hvert tilfelle. Da blir hensikten med hver test tydelig og robust når skjemaet vokser.

<?php
final class UserFactory
{
    public static function create(\PDO $pdo, array $overrides = []): int
    {
        $data = array_merge(['email' => 'user'.uniqid().'@test.dev'], $overrides);
        $stmt = $pdo->prepare('INSERT INTO users (email) VALUES (:email)');
        $stmt->execute($data);
        return (int) $pdo->lastInsertId();
    }
}

Funksjonstester: Kall appen som en klient

En funksjonstest driver den virkelige applikasjonskjernen eller ruteren med en simulert forespørsel og kontrollerer responsen – statuskode, headere og innhold. Symfony sin WebTestCase og Laravels HTTP-testhjelpere gjør nettopp dette uten å starte en ekte webserver. Nedenfor vises Symfony-varianten.

<?php
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

final class HealthControllerTest extends WebTestCase
{
    public function test_health_endpoint_returns_ok(): void
    {
        $client = static::createClient();
        $client->request('GET', '/health');

        self::assertResponseIsSuccessful();        // 2xx
        self::assertJson($client->getResponse()->getContent());
    }
}

Kontrollere responsen

Funksjonstester kontrollerer observerbar atferd ved grensen: status, JSON-struktur og bivirkninger i databasen. Nedenfor oppretter en POST-forespørsel en ressurs; vi kontrollerer både HTTP-responsen og at oppføringen faktisk ble lagret – det beviser at hele stakken samarbeidet.

<?php
public function test_creating_a_user_persists_it(): void
{
    $client = static::createClient();
    $client->request('POST', '/users', server: [
        'CONTENT_TYPE' => 'application/json',
    ], content: json_encode(['email' => 'ada@example.com']));

    self::assertResponseStatusCodeSame(201);

    $repo = static::getContainer()->get(UserRepository::class);
    self::assertNotNull($repo->findByEmail('ada@example.com'));
}

Bruk fakes selektivt i ytterkantene

Selv funksjonstester bør ikke kalle ekte betalingsleverandører eller sende ekte e-post. Erstatt bare de ytterste eksterne tjenestene – bytt dem ut i testcontaineren med fakes eller implementasjoner i minnet – samtidig som egen kode, ruting og database forblir ekte. Slik bevares ende-til-ende-troverdigheten uten ustabile nettverkskall.

<?php
public function test_checkout_charges_via_gateway(): void
{
    $client = static::createClient();
    // Replace the real gateway binding with an in-memory fake:
    static::getContainer()->set(PaymentGateway::class, new FakePaymentGateway());

    $client->request('POST', '/checkout', content: json_encode(['cart' => 1]));
    self::assertResponseIsSuccessful();
}

Separate testsett, ulike hastigheter

Del opp phpunit.xml i testsuiter slik at den raske enhetstestpakken kjører ved hver lagring, mens de langsomme integrasjons-/funksjonstestpakkene kjører ved behov eller i CI. Gruppér etter katalog og velg med --testsuite.

<phpunit>
  <testsuites>
    <testsuite name="unit">
      <directory>tests/Unit</directory>
    </testsuite>
    <testsuite name="integration">
      <directory>tests/Integration</directory>
    </testsuite>
    <testsuite name="functional">
      <directory>tests/Functional</directory>
    </testsuite>
  </testsuites>
</phpunit>

Determinisme er viktigere enn dekning

Den raskeste måten å miste tilliten til en testpakke på er ustabile tester. Gjør integrasjons- og funksjonstester deterministiske:

  • Frys tiden (injiser en klokke) i stedet for å kalle time().
  • Sett et fast frø for tilfeldighet; unngå rekkefølgeavhengige assertions i usorterte spørringer.
  • Tilbakestill databasetilstanden for hver test (tilbakerulling av transaksjon / trunkering).
  • Avheng aldri av rekkefølgen testene kjøres i.

La testdatabasen samsvare med produksjon

SQLite i minnet er raskt, men dialektforskjeller (skiller mellom store og små bokstaver, JSON-funksjoner, håndheving av fremmednøkler og typer) kan skjule feil som bare viser seg på den virkelige motoren. For kode som bruker motorspesifikk SQL bør integrasjonstestene kjøres mot samme motor som i produksjon – vanligvis i en midlertidig Docker-container i CI – i stedet for en praktisk erstatning.

<?php
// Read connection from environment so CI points at a real Postgres/MySQL container
$dsn = getenv('TEST_DATABASE_URL') ?: 'sqlite::memory:';
$pdo = new \PDO($dsn);
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
// In CI, TEST_DATABASE_URL targets the same engine as production.

Kort sjekk

Hvordan bør en funksjonstest behandle en tredjeparts betalingsleverandør?

Oppsummering

De har lært å teste komponenter sammen:

  • Integrasjonstester bruker ekte databaser og tjenester for å fange opp det mocks skjuler; isoler dem med tilbakerulling av transaksjoner.
  • Funksjonstester driver den virkelige kjernen via simulerte forespørsler og kontrollerer responser og bivirkninger.
  • Factories bygger minimale testdata; bruk fakes bare på de ytterste eksterne grensene.
  • Del testpakkene etter hastighet og sørg for at langsommere tester er deterministiske.

Neste tema: måling av testkvalitet med mutasjonstesting.

Gratis å komme i gang

Lær deg PHP med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
49
Leksjoner
195

Ofte stilte spørsmål

Er leksjonen «Integrasjons- og funksjonstesting» gratis?

Ja – hele teksten i «Integrasjons- og funksjonstesting» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av PHP Academy-kurset, kan du oppgradere til CoddyKit PRO. Kurset i PHP Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «Integrasjons- og funksjonstesting»?

Test hvordan komponentene fungerer sammen i praksis. Du øver på PHP Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med PHP Academy?

Ingen tidligere erfaring er nødvendig. PHP Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 3 av 4.

Hvor lang tid tar leksjonen «Integrasjons- og funksjonstesting»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne PHP Academy-leksjonen?

Ja. Alle PHP Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Arbeidsflyten i testdrevet utvikling
  2. Mocking og stubbing med Mockery
  3. Integrasjons- og funksjonstesting
  4. Mutasjonstesting med Infection
← Tilbake til PHP Academy