Testikonteinerid - kuidas muuta testid lihtsamaks?
Bartlomiej Kuczynski
Kas otsite võimalust teha teste lihtsamalt? Meil on teid! Vaadake järgmist artiklit ja õppige, kuidas seda teha.
Kaasaegne rakenduste arendamine põhineb ühel lihtsal reeglil:
Kasutage koostist
Me koostame klassid, funktsioonid ja teenused suuremateks tarkvaratükkideks. See viimane element on mikroteenuste ja kuusnurkne arhitektuur. Me soovime kasutada olemasolevaid lahendusi, integreerida need meie tarkvaraga ja minna otse üle turg.
Kas soovite tegeleda konto registreerimise ja kasutajate andmete säilitamisega? Võite valida ühe OAuthi teenustest. Võib-olla pakub teie rakendus mingit tellimust või makset? On palju teenuseid, mis aitavad teil sellega toime tulla. Kas teil on vaja oma veebisaidil analüütikat, kuid te ei mõista GDPRi? Võtke julgelt üks valmislahendustest.
Midagi, mis teeb arenduse ärilisest vaatenurgast nii lihtsaks, võib teile peavalu valmistada - hetkel, kui teil on vaja kirjutada lihtne test.
Fantastilised elukad: Järjekorrad, andmebaasid ja nende testimine
Ühiktestimine on üsna lihtne. Kui te järgite ainult neid reegleid, siis teie testkeskkond ja kood on terved. Millised on need reeglid?
Lihtne kirjutada - ühikteste peaks olema lihtne kirjutada, sest neid kirjutatakse palju. Vähem vaeva tähendab, et teste kirjutatakse rohkem.
Loetav - testkood peaks olema kergesti loetav. Test on lugu. See kirjeldab tarkvara käitumist ja seda võib kasutada dokumentatsiooni lühendina. Hea ühiktest aitab parandada vigu ilma koodi silumata.
Usaldusväärne - test peaks ebaõnnestuma ainult siis, kui testitavas süsteemis on viga. Ilmselt? Mitte alati. Mõnikord lähevad testid läbi, kui neid ükshaaval käivitada, kuid ebaõnnestuvad, kui neid käivitada komplektina. Nad lähevad teie masinas läbi, kuid CI-s ebaõnnestuvad (Töötab minu masinas). Heal ühiktestil on ainult üks ebaõnnestumise põhjus.
Kiire - testid peaksid olema kiired. Testide ettevalmistamine, käivitamine ja testide teostamine peaks olema väga kiire. Vastasel juhul te kirjutate neid, kuid mitte käivitate neid. Aeglased testid tähendavad kadunud fookust. Sa ootad ja vaatad eduriba.
Sõltumatu - lõpuks peaks test olema sõltumatu. See reegel tuleneb eelmistest. Ainult tõeliselt sõltumatud testid võivad saada ühikuks. Nad ei sega üksteist, neid saab käivitada mis tahes järjekorras ja võimalikud vead ei sõltu teiste testide tulemustest. Sõltumatus tähendab ka seda, et nad ei sõltu mis tahes välistest ressurssidest, nagu andmebaasid, sõnumiteenused või failisüsteem. Kui teil on vaja suhelda väliste vahenditega, võite kasutada mockisid, stubisid või dummy'sid.
Kõik muutub keeruliseks, kui tahame kirjutada mõned integratsioonitestid. See ei ole paha, kui me tahame testida paar teenust koos. Aga kui meil on vaja testida teenuseid, mis kasutavad väliseid ressursse, nagu andmebaasid või sõnumiteenused, siis me küsime probleeme.
Testi läbiviimiseks peate installima...
Aastaid tagasi, kui tahtsime teha integratsiooniteste ja kasutada näiteks andmebaase, oli meil kaks võimalust:
Me võime paigaldada andmebaasi lokaalselt. Seadistame skeemi ja ühendame selle meie testist;
Me saame ühendada olemasoleva instantsi "kusagil kosmoses".
Mõlemal olid plussid, mõlemal olid miinused. Kuid mõlemad toovad kaasa täiendava keerukuse. Mõnikord oli see tehniline keerukus, mis tulenes teatavate vahendite omadustest, nt Oracle DB paigaldamine ja haldamine teie localhostil. Mõnikord oli see ebamugavus protsessis, nt tuleb kokku leppida testi meeskond JMS-i kasutamise kohta... iga kord, kui soovite teste käivitada.
Konteinerid päästmiseks
Viimase 10 aasta jooksul on konteinerite idee saanud tööstuses tunnustust. Seega on loomulik otsus valida konteinerid lahendusena meie integratsioonitesti probleemile. See on lihtne ja puhas lahendus. Te lihtsalt käivitate oma protsessi buildi ja kõik toimib! Te ei suuda seda uskuda? Vaadake seda lihtsat maven buildi konfiguratsiooni:
com.dkanejs.maven.plugins
docker-compose-maven-plugin
4.0.0
up
test-compile
up
${projekt.basedir}/docker-compose.yml
true
down
post-integration-test
down
${project.basedir}/docker-compose.yml
true
Ja docker-compose.yml fail näeb ka päris kena välja!
Ülaltoodud näide on väga lihtne. Ainult üks postgres'i andmebaas, pgAdmin ja see on kõik. Kui te käivitate
bash
$ mvn clean verify
siis käivitab maven plugin konteinerid ja pärast teste lülitab need välja. Probleemid algavad siis, kui projekt kasvab ja ka meie kompositsioonifail kasvab. Iga kord tuleb käivitada kõik konteinerid ja nad jäävad kogu buildi jooksul ellu. Olukorda saab veidi parandada, kui muuta pluginate täitmise konfiguratsiooni, kuid sellest ei piisa. Halvimal juhul ammendavad teie konteinerid süsteemi ressursid enne testide käivitamist!
Ja see ei ole ainus probleem. Te ei saa oma IDE-st käivitada ühtegi integratsioonitesti. Enne seda tuleb konteinerid käsitsi käivitada. Lisaks sellele lammutab järgmine maven'i käivitamine need konteinerid (vaadake alla täitmine).
Seega on see lahendus nagu suur kaubalaev. Kui kõik toimib hästi, siis on kõik ok. Igasugune ootamatu või ebatavaline käitumine viib meid mingi katastroofini.
Aga mis oleks, kui me saaksime oma konteinereid testidest käivitada? See idee tundub hea ja seda on juba rakendatud. Testkonteinerid, sest me räägime sellest projektist, siin on lahendus meie probleemidele. Ei ole ideaalne, kuid keegi ei ole täiuslik.
See on Java raamatukogu, mis toetab JUnit ja Spock teste, pakkudes kerget ja lihtsat võimalust Dockeri konteineri käivitamiseks. Vaatame seda ja kirjutame koodi!
Eeldused ja konfiguratsioon
Enne alustamist peame kontrollima oma konfiguratsiooni. Testimismahutid vajadus:
Docker versioonis v17.09,
Java minimaalne versioon 1.8,
Juurdepääs võrgule, eriti docker.hubile.
Rohkem teavet konkreetsete operatsioonisüsteemide ja CI nõuete kohta leiate järgmiselt. aadressil dokumentatsioon.
Nüüd on aeg lisada mõned read, et pom.xml.
org.testcontainers
testcontainers-bom
${testcontaines.version}
pom
import
org.postgresql
postgresql
runtime
org.testcontainers
postgresql
test
org.testcontainers
junit-jupiter
test
Ma kasutan Testimismahutid versioon 1.17.3, kuid võite vabalt kasutada uusimat.
Testid Postgres konteineriga
Esimene samm on meie konteineri instantsi ettevalmistamine. Seda võib teha otse testis, kuid iseseisev klass tundub parem.
public class Postgres13TC extends PostgreSQLContainer {
private static final Postgres13TC TC = new Postgres13TC();
private Postgres13TC() {
super("postgres:13.2");
}
public static Postgres13TC getInstance() {
return TC;
}
@Override
public void start() {
super.start();
System.setProperty("DB_URL", TC.getJdbcUrl());
System.setProperty("DB_USERNAME", TC.getUsername());
System.setProperty("DB_PASSWORD", TC.getPassword());
}
@Override
public void stop() {
// ei tee midagi. See on jagatud instants. Las JVM tegeleb selle toiminguga.
}
}
Testide alguses loome instantsi Postgres13TC. See klass saab käsitleda teavet meie konteineri kohta. Kõige olulisemad on siin andmebaasiühenduste stringid ja volikirjaandmed. Nüüd on aeg kirjutada väga lihtne test.
Ma kasutan siin JUnit 5. Annotatsioon @Testikonteinerid on osa laiendustest, mis kontrollivad konteinereid testkeskkonnas. Nad leiavad kõik väljad koos @Konteiner märkus ja vastavalt start- ja stop-konteinerid.
Testid Spring Bootiga
Nagu ma juba mainisin, kasutan projektis Spring Boot'i. Sel juhul tuleb kirjutada veidi rohkem koodi. Esimene samm on luua täiendav konfiguratsiooniklass.
See klass tühistab olemasolevad omadused väärtustega, mis pärinevad klassist testkonteiner. Esimesed kolm omadust on tavalised Springi omadused. Järgmised viis on täiendavad, kohandatud omadused, mida saab kasutada teiste ressursside ja laienduste, näiteks liquibase'i konfigureerimiseks:
@SpringBootTest(webEnvironment = RANDOM_PORT)
@AutoConfigureTestDatabase(replace = NONE)
@ContextConfiguration(initializers = ContainerInit.class)
@Testikonteinerid
class DummyRepositoryTest {
@Autowired
private DummyRepository;
@Test
void shouldReturnDummy() {
var byId = dummyRepository.getById(10L);
var expected = new Dummy();
expected.setId(10L);
assertThat(byId).completes().emitsCount(1).emits(expected);
}
}
Meil on siin mõned lisamärkused.
@SpringBootTest(webEnvironment = RANDOM_PORT) - tähistab testi kui Spring Boot testi ja käivitab Springi konteksti.
@AutoConfigureTestDatabase(replace = NONE) - need märked ütlevad, et spring test laiendus ei tohiks asendada postgres andmebaasi konfiguratsiooni H2 mälu konfiguratsiooniga.
@ContextConfiguration(initializers = ContainerInit.class) - täiendav kevadine kontekst konfiguratsioon, kus me seadistame omadused alates Testimismahutid.
@Testikonteinerid - nagu eelnevalt mainitud, kontrollib see märkus konteineri elutsüklit.
Selles näites kasutan ma reaktiivseid repositooriume, kuid see toimib samamoodi ka tavaliste JDBC- ja JPA-repositooriumidega.
Nüüd saame selle testi käivitada. Kui tegemist on esimese käivitamisega, peab mootor tõmbama kujutised docker.hubist. See võib võtta hetke. Pärast seda näeme, et kaks konteinerit on käivitunud. Üks on postgres ja teine on Testcontainers kontroller. See teine konteiner haldab jooksvaid konteinereid ja isegi kui JVM ootamatult peatub, siis ta lülitab konteinerid välja ja koristab keskkonna.
Võtame kokku
Testimismahutid on väga lihtsalt kasutatavad tööriistad, mis aitavad meil luua integratsiooniteste, mis kasutavad Dockeri konteinereid. See annab meile rohkem paindlikkust ja suurendab arenduskiirust. Testi konfiguratsiooni õige seadistamine vähendab uute arendajate pardaloleku aega. Nad ei pea seadistama kõiki sõltuvusi, vaid lihtsalt käivitama kirjutatud testid valitud konfiguratsioonifailidega.