Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Testien organisointi

Kuten luvun alussa mainittiin, testaus on monimutkainen ala, ja eri ihmiset käyttävät eri terminologiaa ja organisointia. Rust-yhteisö ajattelee testejä kahden pääkategorian kautta: yksikkötestit ja integraatiotestit. Yksikkötestit ovat pieniä ja keskittyneempiä, testaten yhtä moduulia erillään muusta koodista kerrallaan, ja ne voivat testata yksityisiä rajapintoja. Integraatiotestit ovat täysin kirjastosi ulkopuolella ja käyttävät koodiasi samalla tavalla kuin mikä tahansa muu ulkoinen koodi, käyttäen vain julkista rajapintaa ja mahdollisesti testaten useita moduuleja per testi.

Molempien testityyppien kirjoittaminen on tärkeää varmistaaksesi, että kirjastosi osat tekevät odottamasi asiat erikseen ja yhdessä.

Yksikkötestit

Yksikkötestien tarkoitus on testata jokainen koodin yksikkö erillään muusta koodista nopeasti paikantaakseen, missä koodi toimii ja missä ei odotetulla tavalla. Laitat yksikkötestit src-hakemistoon kuhunkin tiedostoon sen koodin kanssa, jota ne testaavat. Käytäntö on luoda tests-niminen moduuli kuhunkin tiedostoon testifunktioiden säilyttämiseksi ja merkitä moduuli cfg(test)- attribuutilla.

tests-moduuli ja #[cfg(test)]

#[cfg(test)]-merkintä tests-moduulissa kertoo Rustille kääntää ja ajaa testikoodin vain, kun ajat cargo test, ei kun ajat cargo build. Tämä säästää käännösaikaa, kun haluat vain rakentaa kirjaston, ja säästää tilaa syntyneessä käännettyssä artefaktissa, koska testejä ei sisällytetä. Näet, että koska integraatiotestit menevät eri hakemistoon, niillä ei tarvitse olla #[cfg(test)]- merkintää. Koska yksikkötestit menevät samoihin tiedostoihin koodin kanssa, käytät #[cfg(test)]:ää määrittääksesi, ettei niitä sisällytetä käännettyyn tulokseen.

Muista, että kun loimme uuden adder-projektin tämän luvun ensimmäisessä osiossa, Cargo loi meille tämän koodin:

Filename: src/lib.rs

{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-01/src/lib.rs}}

Automaattisesti luodussa tests-moduulissa cfg-attribuutti tarkoittaa konfiguraatiota (configuration) ja kertoo Rustille, että seuraava kohde tulisi sisällyttää vain tietyn konfiguraatiovaihtoehdon ollessa voimassa. Tässä tapauksessa konfiguraatiovaihtoehto on test, jonka Rust tarjoaa testien kääntämiseen ja ajamiseen. Käyttämällä cfg-attribuuttia Cargo kääntää testikoodimme vain, jos aktiivisesti ajamme testit cargo test -komennolla. Tämä sisältää kaikki apufunktiot, jotka voivat olla tässä moduulissa, sekä #[test]-merkityt funktiot.

Yksityisten funktioiden testaaminen

Testausyhteisössä on keskustelua siitä, pitäisikö yksityisiä funktioita testata suoraan, ja muut kielet tekevät yksityisten funktioiden testaamisesta vaikeaa tai mahdotonta. Riippumatta siitä, mihin testausideologiaan sitoudut, Rustin näkyvyyssäännöt sallivat yksityisten funktioiden testaamisen. Harkitse listauksen 11-12 koodia yksityisellä internal_adder-funktiolla.

Filename: src/lib.rs
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-12/src/lib.rs}}
Listing 11-12: Yksityisen funktion testaaminen

Huomaa, että internal_adder-funktiota ei ole merkitty pub-määritteellä. Testit ovat vain Rust-koodia, ja tests-moduuli on vain toinen moduuli. Kuten käsiteltiin osiossa ”Polut moduulipuun kohteeseen viittaamiseen”, lapsimoduulien kohteet voivat käyttää esi-isämoduuliensa kohteita. Tässä testissä tuomme kaikki tests-moduulin vanhemman moduulin kohteet näkyviin use super::* -lauseella, ja sitten testi voi kutsua internal_adder-funktiota. Jos et mielestäsi yksityisiä funktioita pitäisi testata, Rustissa ei ole mitään, mikä pakottaisi sinua tekemään niin.

Integraatiotestit

Rustissa integraatiotestit ovat täysin kirjastosi ulkopuolella. Ne käyttävät kirjastoasi samalla tavalla kuin mikä tahansa muu koodi, mikä tarkoittaa, että ne voivat kutsua vain kirjastosi julkisen API:n funktioita. Niiden tarkoitus on testata, toimivatko kirjastosi monet osat yhdessä oikein. Koodin yksiköt, jotka toimivat oikein yksinään, voivat aiheuttaa ongelmia integroituna, joten integroidun koodin testikattavuus on myös tärkeää. Luodaksesi integraatiotestejä tarvitset ensin tests-hakemiston.

tests-hakemisto

Luomme tests-hakemiston projektihakemiston ylätasolle, src-hakemiston viereen. Cargo tietää etsiä integraatiotestitiedostoja tästä hakemistosta. Voimme sitten tehdä niin monta testitiedostoa kuin haluamme, ja Cargo kääntää jokaisen tiedoston erillisenä cratena.

Luodaan integraatiotesti. Kun listauksen 11-12 koodi on vielä src/lib.rs- tiedostossa, tee tests-hakemisto ja luo uusi tiedosto nimeltä tests/integration_test.rs. Hakemistorakenteesi pitäisi näyttää tältä:

adder
├── Cargo.lock
├── Cargo.toml
├── src
│   └── lib.rs
└── tests
    └── integration_test.rs

Syötä listauksen 11-13 koodi tests/integration_test.rs-tiedostoon.

Filename: tests/integration_test.rs
{{#rustdoc_include ../listings/ch11-writing-automated-tests/listing-11-13/tests/integration_test.rs}}
Listing 11-13: Integraatiotesti adder-craten funktiolle

Jokainen tiedosto tests-hakemistossa on erillinen crate, joten meidän täytyy tuoda kirjastomme kunkin testicraten näkyvyysalueelle. Siksi lisäämme use adder::add_two; koodin alkuun, jota emme tarvinne yksikkötesteissä.

Emme tarvitse merkitä mitään koodia tests/integration_test.rs-tiedostossa #[cfg(test)]-attribuutilla. Cargo käsittelee tests-hakemistoa erityisesti ja kääntää tämän hakemiston tiedostot vain, kun ajamme cargo test. Aja cargo test nyt:

{{#include ../listings/ch11-writing-automated-tests/listing-11-13/output.txt}}

Tulosteen kolme osiota sisältävät yksikkötestit, integraatiotestin ja dokumentaatiotestit. Huomaa, että jos mikä tahansa testi osiossa epäonnistuu, seuraavia osioita ei ajeta. Esimerkiksi jos yksikkötesti epäonnistuu, integraatio- ja dokumentaatiotesteistä ei ole tulostetta, koska nämä testit ajetaan vain, jos kaikki yksikkötestit läpäisevät.

Yksikkötestien ensimmäinen osio on sama kuin olemme nähneet: yksi rivi kullekin yksikkötestille (yksi nimeltä internal, jonka lisäsimme listauksessa 11-12) ja sitten yhteenvetorivi yksikkötesteille.

Integraatiotestien osio alkaa rivillä Running tests/integration_test.rs. Seuraavaksi on rivi kullekin integraatiotestin testifunktiolle ja yhteenvetorivi integraatiotestin tuloksille juuri ennen kuin Doc-tests adder- osio alkaa.

Jokaisella integraatiotestitiedostolla on oma osionsa, joten jos lisäämme lisää tiedostoja tests-hakemistoon, tulee lisää integraatiotestien osioita.

Voimme silti ajaa tietyn integraatiotestifunktion määrittämällä testifunktion nimen argumentiksi cargo test -komennolle. Ajaaaksesi kaikki testit tietyssä integraatiotestitiedostossa, käytä cargo test -komennon --test-argumenttia ja tiedoston nimeä:

{{#include ../listings/ch11-writing-automated-tests/output-only-05-single-integration/output.txt}}

Tämä komento ajaa vain tests/integration_test.rs-tiedoston testit.

Alimodulit integraatiotesteissä

Kun lisäät integraatiotestejä, saatat haluta tehdä lisää tiedostoja tests- hakemistoon järjestääksesi niitä; esimerkiksi voit ryhmitellä testifunktiot sen toiminnallisuuden mukaan, jota ne testaavat. Kuten aiemmin mainittiin, jokainen tiedosto tests-hakemistossa käännetään omana erillisenä cratenaan, mikä on hyödyllistä erillisten näkyvyysalueiden luomiseen, jotka jäljittelevät läheisemmin sitä, miten loppukäyttäjät käyttävät crateasi. Tämä tarkoittaa kuitenkin, että tests-hakemiston tiedostot eivät jaa samaa käyttäytymistä kuin src-hakemiston tiedostot, kuten opit luvussa 7 koodin jakamisesta moduuleihin ja tiedostoihin.

tests-hakemiston tiedostojen erilainen käyttäytyminen näkyy selvimmin, kun sinulla on joukko apufunktioita useissa integraatiotestitiedostoissa, ja yrität seurata luvun 7 osion ”Moduulien erottaminen eri tiedostoihin” vaiheita erottaaksesi ne yhteiseen moduuliin. Esimerkiksi jos luomme tests/common.rs-tiedoston ja sijoitamme siihen setup-nimisen funktion, voimme lisätä setup-funktioon koodia, jota haluamme kutsua useista testifunktioista useissa testitiedostoissa:

Filename: tests/common.rs

{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-12-shared-test-code-problem/tests/common.rs}}

Kun ajamme testit uudelleen, näemme uuden osion testitulosteessa common.rs- tiedostolle, vaikka tässä tiedostossa ei ole testifunktioita eikä me kutsunut setup-funktiota mistään:

{{#include ../listings/ch11-writing-automated-tests/no-listing-12-shared-test-code-problem/output.txt}}

common-näkyminen testituloksissa running 0 tests -tulosteella ei ole sitä, mitä halusimme. Halusimme vain jakaa koodia muiden integraatiotestitiedostojen kanssa. Välttääksemme common-näkymisen testitulosteessa, sen sijaan että loisimme tests/common.rs-tiedoston, luomme tests/common/mod.rs-tiedoston. Projektihakemisto näyttää nyt tältä:

├── Cargo.lock
├── Cargo.toml
├── src
│   └── lib.rs
└── tests
    ├── common
    │   └── mod.rs
    └── integration_test.rs

Tämä on vanhempi nimeämiskäytäntö, jonka Rust myös ymmärtää ja josta mainittiin luvun 7 osiossa ”Vaihtoehtoiset tiedostopolut”. Tämän niminen tiedosto kertoo Rustille, ettei se käsittele common-moduulia integraatiotestitiedostona. Kun siirrämme setup-funktion koodin tests/common/mod.rs-tiedostoon ja poistamme tests/common.rs-tiedoston, testitulosteen osio ei enää näy. tests-hakemiston alihakemistojen tiedostoja ei käännetä erillisinä crateina eikä niillä ole osioita testitulosteessa.

Kun olemme luoneet tests/common/mod.rs-tiedoston, voimme käyttää sitä miltä tahansa integraatiotestitiedostolta moduulina. Tässä on esimerkki setup-funktion kutsumisesta it_adds_two-testistä tests/integration_test.rs- tiedostossa:

Filename: tests/integration_test.rs

{{#rustdoc_include ../listings/ch11-writing-automated-tests/no-listing-13-fix-shared-test-code-problem/tests/integration_test.rs}}

Huomaa, että mod common; -ilmoitus on sama kuin moduuli-ilmoitus, jonka demonstroimme listauksessa 7-21. Sitten testifunktiossa voimme kutsua common::setup()-funktiota.

Integraatiotestit binääricrateille

Jos projektimme on binääricrate, joka sisältää vain src/main.rs-tiedoston eikä src/lib.rs-tiedostoa, emme voi luoda integraatiotestejä tests- hakemistoon ja tuoda src/main.rs-tiedostossa määriteltyjä funktioita näkyviin use-lauseella. Vain kirjastocratet paljastavat funktioita, joita muut cratet voivat käyttää; binääricratet on tarkoitettu ajettaviksi itsenäisesti.

Tämä on yksi syy siihen, miksi Rust-projektit, jotka tarjoavat binäärin, käyttävät suoraviivaista src/main.rs-tiedostoa, joka kutsuu logiikkaa, joka asuu src/lib.rs-tiedostossa. Tällä rakenteella integraatiotestit voivat testata kirjastocratea use-lauseella tuodakseen tärkeän toiminnallisuuden näkyviin. Jos tärkeä toiminnallisuus toimii, pieni määrä koodia src/main.rs-tiedostossa toimii myös, eikä sitä pientä koodimäärää tarvitse testata.

Yhteenveto

Rustin testausominaisuudet tarjoavat tavan määrittää, miten koodin pitäisi toimia varmistaaksesi, että se jatkaa toimimista odottamallasi tavalla, vaikka teet muutoksia. Yksikkötestit testaavat kirjaston eri osia erikseen ja voivat testata yksityisiä toteutustietoja. Integraatiotestit tarkistavat, että kirjaston monet osat toimivat yhdessä oikein, ja ne käyttävät kirjaston julkista API:a testatakseen koodia samalla tavalla kuin ulkoinen koodi käyttää sitä. Vaikka Rustin tyyppijärjestelmä ja omistajuussäännöt auttavat estämään joitakin virhetyyppejä, testit ovat silti tärkeitä vähentämään logiikkavirheitä, jotka liittyvät siihen, miten koodisi odotetaan käyttäytyvän.

Yhdistetään tässä luvussa ja aiemmissa luvuissa oppimasi tieto ja työskennellään projektin parissa!