PHP Academy · Oppitunti

Integraatio- ja toiminnallinen testaus

Testatkaa, miten komponentit toimivat yhdessä käytännössä.

Oppitunti 3/413 vaihetta

Integraatio- ja toiminnallinen testaus on ilmainen PHP Academy-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu PHP Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. PHP Academy-kurssilla on yhteensä 4 oppituntia.

Yksikkötestausta pidemmälle

Yksikkötestit osoittavat, että luokka toimii eristyksissä, kun kaikki riippuvuudet on korvattu mockeilla. Mockit voivat kuitenkin johtaa harhaan — oikea tietokanta, frameworkin reititin tai HTTP-kerros toimii eri tavalla. Integraatiotestit varmistavat, että vähintään kaksi oikeaa komponenttia toimii yhteen kytkettynä; toiminnalliset testit käyvät sovelluksen läpi päästä päähän sen julkisen sisääntulopisteen (HTTP-pyyntö tai konsolikomento) kautta. Tässä oppitunnissa käsitellään ja kirjoitetaan molempia.

Testipyramidi

Hyvä testikokoelma on pyramidin muotoinen:

  • Paljon yksikkötestejä — nopeita, eristettyjä ja jatkuvasti ajettavia.
  • Vähemmän integraatiotestejä — oikea tietokanta, jono ja välimuisti; hitaampia.
  • Vähän toiminnallisia/E2E-testejä — koko pino, hitaimpia ja herkimpiä rikkoutumaan.

Tämän kääntäminen toisinpäin, jolloin syntyy pääasiassa E2E-testeistä koostuva jäätelötötterö, tuottaa hitaan ja epävakaan testikokoelman. Siirtäkää kattavuutta alemmille tasoille aina kun mahdollista.

Integraatio: repository oikeaa tietokantaa vasten

Repositoryn integraatiotesti käyttää oikeaa tietokantayhteyttä — ei mockia — ja paljastaa SQL-, skeema- ja yhdistämisvirheitä. Käyttäkää erillistä testitietokantaa (tai muistissa toimivaa SQLite-tietokantaa, joka vastaa käyttämäänne murretta riittävän hyvin) ja oikeaa PDO:ta.

<?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);
    }
}

Eristys: transaktion palauttaminen

Integraatiotestit eivät saa vuotaa tilaa toisiinsa. Nopein tapa on kääriä jokainen testi transaktion sisään ja tehdä rollback tearDown()-metodissa — tietokanta palautuu puhtaaseen alkutilaan ilman uudelleentäyttöä. (Trunkkaus on varavaihtoehto, jos testattava koodi tekee commitin tai käyttää 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
}

Testidata ja factoryt

Testit tarvitsevat dataa. Välttäkää hauraita valtavia SQL-vedoksia ja suosikaa pieniä factoryja, jotka luovat vain testin tarvitsemat rivit ja tarjoavat järkevät oletusarvot, joita voi muuttaa tapauskohtaisesti. Näin jokaisen testin tarkoitus pysyy selvänä, ja testit kestävät skeeman kasvamisen.

<?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();
    }
}

Toiminnallinen testaus: sovellusta ajetaan asiakkaan tavoin

Toiminnallinen testi käyttää oikeaa sovelluksen kerneliä ja reititintä simuloidulla pyynnöllä ja tarkistaa vastauksesta tilakoodin, otsakkeet ja rungon. Symfonyn WebTestCase ja Laravelin HTTP-testauksen apufunktiot tekevät juuri tämän käynnistämättä oikeaa web-palvelinta. Alla on Symfony-rakenne.

<?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());
    }
}

Vastauksen tarkistaminen

Toiminnallisissa testeissä tarkistetaan rajapinnan havaittava toiminta: tila, JSON-rakenne ja tietokantaan kohdistuvat sivuvaikutukset. Alla oleva POST luo resurssin; tarkistamme sekä HTTP-vastauksen että sen, että tietue todella tallentui tietokantaan — näin varmistamme koko pinon toimineen yhdessä.

<?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'));
}

Ulkoisten rajojen valikoiva feikkaus

Toiminnallisten testienkaan ei pitäisi kutsua oikeita maksupalveluntarjoajia tai lähettää oikeita sähköposteja. Korvatkaa vain uloimmat ulkoiset palvelut — vaihtakaa ne testikontissa feikkeihin tai muistissa toimiviin toteutuksiin — ja pitäkää oma koodinne, reitityksenne ja tietokantanne oikeina. Näin päästä päähän -uskollisuus säilyy ilman epävakaita verkkokutsuja.

<?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();
}

Erilliset testikokoelmat ja nopeudet

Jakakaa phpunit.xml testikokoelmiin, jotta nopea yksikkötestikokoelma voidaan ajaa jokaisen tallennuksen yhteydessä ja hitaat integraatio- ja toiminnalliset testikokoelmat tarvittaessa tai CI:ssä. Ryhmitelkää testit hakemistoittain ja valitkaa ne --testsuite-valitsimella.

<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>

Deterministisyys on kattavuutta tärkeämpää

Nopein tapa menettää luottamus testikokoelmaan on testien epävakaus. Tehkää integraatio- ja toiminnallisista testeistä deterministisiä:

  • Jäädyttäkää aika injektoimalla kello sen sijaan, että kutsuisitte time()-funktiota.
  • Asettakaa satunnaislukugeneraattorin siemen ja välttäkää järjestämättömien kyselyjen tulosten järjestyksestä riippuvia tarkistuksia.
  • Palauttakaa tietokannan tila jokaisen testin jälkeen (transaktion rollback tai trunkkaus).
  • Älkää koskaan riippuko testien suoritusjärjestyksestä.

Testitietokannan on vastattava tuotantoa

Muistissa toimiva SQLite on nopea, mutta murteiden väliset erot (kirjainkokoherkkyys, JSON-funktiot, viiteavainten valvonta ja tyypit) voivat peittää virheitä, jotka tulevat esiin vasta oikeassa moottorissa. Jos koodinne käyttää moottorikohtaista SQL:ää, ajakaa integraatiotestit samalla moottorilla kuin tuotannossa — CI:ssä tämä on usein kertakäyttöinen Docker-kontti — kätevän korvikkeen sijaan.

<?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.

Pikatarkistus

Miten toiminnallisessa testissä pitäisi käsitellä kolmannen osapuolen maksupalveluntarjoajaa?

Kertaus

Opitte testaamaan komponentteja yhdessä:

  • Integraatiotestit käyttävät oikeita tietokantoja ja palveluita paljastaakseen sen, minkä mockit peittävät; eristäkää ne transaktion rollbackilla.
  • Toiminnalliset testit käyttävät oikeaa kerneliä simuloitujen pyyntöjen kautta ja tarkistavat vastaukset sekä sivuvaikutukset.
  • Factoryt luovat mahdollisimman vähän testidataa; feikatkaa vain uloimmat ulkoiset rajapinnat.
  • Jakakaa testikokoelmat nopeuden perusteella ja pitäkää hitaammat testit deterministisinä.

Seuraavaksi: testien laadun mittaaminen mutaatiotestauksella.

Aloita maksutta

Opi PHP tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
49
Oppitunnit
195

Usein kysytyt kysymykset

Onko oppitunti ”Integraatio- ja toiminnallinen testaus” ilmainen?

Kyllä – oppitunnin ”Integraatio- ja toiminnallinen testaus” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko PHP Academy-kurssin, päivitä CoddyKit PROhon. PHP Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Integraatio- ja toiminnallinen testaus”?

Testatkaa, miten komponentit toimivat yhdessä käytännössä. Harjoittelet PHP Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni PHP Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin PHP Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.

Kuinka kauan ”Integraatio- ja toiminnallinen testaus”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä PHP Academy-oppitunnilla?

Kyllä. Jokainen PHP Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Testiohjatun kehityksen työnkulku
  2. Mockaus ja stubit Mockerylla
  3. Integraatio- ja toiminnallinen testaus
  4. Mutaatiotestaus Infectionilla
← Takaisin: PHP Academy