Hledáte způsob, jak jednodušeji provádět testy? Máme pro vás řešení! Podívejte se do následujícího článku a zjistěte, jak to provést.
Moderní vývoj aplikací je založen na jednom jednoduchém pravidle:
Použijte složení
Třídy, funkce a služby skládáme do větších celků softwaru. Tento poslední prvek je základem mikroslužby a šestiúhelníková architektura. Rádi bychom využili stávající řešení, integrovali je s naším softwarem a přešli rovnou na trh. trh.
Chcete zpracovávat registraci účtů a ukládat data uživatelů? Můžete si vybrat jednu ze služeb OAuth. Možná vaše aplikace nabízí nějaký druh předplatného nebo platby? Existuje mnoho služeb, které vám s tím mohou pomoci. Potřebujete na svém webu nějakou analytiku, ale nerozumíte GDPR? Neváhejte a vezměte si jedno z hotových řešení.
Z něčeho, co z obchodního hlediska tak usnadňuje vývoj, vás může bolet hlava - ve chvíli, kdy potřebujete napsat jednoduchý test.
Fantastická zvířata: Fronty, databáze a jejich testování
Testování jednotek je poměrně jednoduché. Pokud se budete řídit pouze těmito pravidly, pak vaše testovací prostředí a kód jsou zdravé. Jaká jsou to pravidla?
Snadné psaní - jednotkový test by měl být snadný na psaní, protože jich píšete hodně. Méně úsilí znamená, že se napíše více testů.
Čitelný - testovací kód by měl být snadno čitelný. Test je příběh. Popisuje chování softwaru a mohl by sloužit jako dokumentační zkratka. Dobrý jednotkový test pomáhá opravovat chyby bez nutnosti ladění kódu.
Spolehlivé - test by měl selhat pouze v případě, že se v testovaném systému vyskytuje chyba. Je to zřejmé? Ne vždy. Někdy testy projdou, pokud je spustíte jeden po druhém, ale selžou, pokud je spustíte jako sadu. Na vašem počítači projdou, ale na CI selžou (Funguje na mém počítači). Dobrý jednotkový test má pouze jeden důvod selhání.
Rychle - testy by měly být rychlé. Příprava na spuštění, spuštění a samotné provedení testu by mělo být velmi rychlé. Jinak je sice napíšete, ale nespustíte. Pomalé testy znamenají ztracené soustředění. Čekáte a díváte se na ukazatel průběhu.
Nezávislé - konečně, test by měl být nezávislý. Toto pravidlo vychází z předchozích. Pouze skutečně nezávislé testy se mohou stát jednotkou. Vzájemně se neruší, mohou být spuštěny v libovolném pořadí a jejich případné selhání nezávisí na výsledcích jiných testů. Nezávislost také znamená, že nejsou závislé na žádných externích zdrojích, jako jsou databáze, služby přenosu zpráv nebo souborový systém. Pokud potřebujete komunikovat s externími zdroji, můžete použít mocky, stuby nebo atrapy.
Vše se zkomplikuje, když chceme napsat integrační testy. Není to špatné, pokud bychom chtěli testovat několik služeb dohromady. Když ale potřebujeme testovat služby, které využívají externí zdroje, jako jsou databáze nebo služby pro zasílání zpráv, koledujeme si o problémy.
Pro spuštění testu je třeba nainstalovat...
Když jsme před mnoha lety chtěli provést integrační testy a použít např. databáze, měli jsme dvě možnosti:
Databázi můžeme nainstalovat lokálně. Nastavíme schéma a připojíme se z našeho testu;
Můžeme se připojit k existující instanci "někde ve vesmíru".
Obě měly klady i zápory. Obě však přinášejí další úrovně složitosti. Někdy to byla technická složitost vyplývající z vlastností určitých nástrojů, např. instalace a správa DB Oracle na lokálním hostiteli. Někdy šlo o nepříjemnosti v procesu, např. je třeba se dohodnout s testovacím tým o použití JMS... pokaždé, když chcete spustit testy.
Kontejnery na pomoc
V posledních deseti letech se v oboru prosadila myšlenka kontejnerizace. Proto je přirozeným rozhodnutím zvolit kontejnery jako řešení našeho problému s integračními testy. Jedná se o jednoduché a čisté řešení. Stačí spustit sestavení procesu a vše funguje! Nechce se vám tomu věřit? Podívejte se na tuto jednoduchou konfiguraci sestavení maven:
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
Výše uvedený příklad je velmi jednoduchý. Stačí jedna databáze Postgres, pgAdmin a to je vše. Když spustíte
bash
$ mvn clean verify
pak zásuvný modul maven spustí kontejnery a po testech je vypne. Problémy začnou, když se projekt rozroste a náš soubor compose se rozroste také. Pokaždé bude třeba spustit všechny kontejnery a ty budou živé po celou dobu sestavování. Situaci můžete trochu zlepšit změnou konfigurace spouštění zásuvného modulu, ale to nestačí. V nejhorším případě vaše kontejnery vyčerpají systémové prostředky ještě před spuštěním testů!
A to není jediný problém. Z IDE nelze spustit jediný integrační test. Předtím musíte kontejnery spustit ručně. Navíc při dalším spuštění Mavenu se tyto kontejnery rozpadnou (podívejte se na stránku dolů provedení).
Toto řešení je tedy jako velká nákladní loď. Pokud vše funguje dobře, je to v pořádku. Jakékoli neočekávané nebo neobvyklé chování vede k tomu. nás k nějaké katastrofě.
Testovací kontejnery - spouštění kontejnerů z testů
Ale co kdybychom mohli spouštět naše kontejnery z testů? Tento nápad vypadá dobře a již se realizuje. Testovací kontejnery, protože mluvíme o tomto projektu, zde je řešení našich problémů. Není ideální, ale nikdo není dokonalý.
Jedná se o Java knihovna, která podporuje testy JUnit a Spock a poskytuje lehké a snadné způsoby, jak spustit Docker kontejner. Pojďme se na to podívat a napsat nějaký kód!
Předpoklady a konfigurace
Než začneme, musíme zkontrolovat naši konfiguraci. Zkušební kontejnery potřebují:
Docker ve verzi v17.09,
Java minimálně verze 1.8,
Přístup k síti, zejména k docker.hub.
Více informací o požadavcích na konkrétní operační systém a CI naleznete zde. na adrese dokumentace.
Nyní je čas přidat několik řádků do. pom.xml.
org.testcontainers
testcontainers-bom
${testcontaines.version}
pom
import
org.postgresql
postgresql
runtime
org.testcontainers
postgresql
test
org.testcontainers
junit-jupiter
test
Používám Zkušební kontejnery verze 1.17.3, ale klidně použijte ten nejnovější.
Testy s kontejnerem Postgres
Prvním krokem je příprava instance kontejneru. To můžeme udělat přímo v testu, ale lépe vypadá nezávislá třída.
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() {
// nic nedělat. Toto je sdílená instance. Nechte tuto operaci na JVM.
}
}
Na začátku testů vytvoříme instanci příkazu Postgres13TC. Tato třída může zpracovávat informace o našem kontejneru. Nejdůležitější jsou zde řetězce připojení k databázi a pověření. Nyní je čas napsat velmi jednoduchý test.
Používám zde JUnit 5. Anotace @Testcontainers je součástí rozšíření, která řídí kontejnery v testovacím prostředí. Najdou všechna pole s @Container anotace a kontejnery start a stop.
Testy se Spring Boot
Jak jsem již zmínil, v projektu používám Spring Boot. V tomto případě musíme napsat trochu více kódu. Prvním krokem je vytvoření další konfigurační třídy.
Tato třída přepisuje existující vlastnosti hodnotami ze třídy testovací kontejner. První tři vlastnosti jsou standardní vlastnosti jara. Dalších pět jsou doplňkové, vlastní vlastnosti, které lze použít ke konfiguraci dalších zdrojů a rozšíření, jako je např. liquibase:
@SpringBootTest(webEnvironment = RANDOM_PORT)
@AutoConfigureTestDatabase(replace = NONE)
@ContextConfiguration(initializers = ContainerInit.class)
@Testcontainers
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);
}
}
Máme zde několik dalších poznámek.
@SpringBootTest(webEnvironment = RANDOM_PORT) - označí test jako test Spring Boot a spustí kontext spring.
@AutoConfigureTestDatabase(replace = NONE) - tyto anotace říkají, že rozšíření spring test by nemělo v konfiguraci paměti nahrazovat konfiguraci databáze postgres konfigurací H2.
@ContextConfiguration(initializers = ContainerInit.class) - další jarní kontext konfigurace, kde nastavujeme vlastnosti z Zkušební kontejnery.
@Testcontainers - jak již bylo uvedeno, tato anotace řídí životní cyklus kontejneru.
V tomto příkladu používám reaktivní úložiště, ale stejně to funguje i s běžnými úložišti JDBC a JPA.
Nyní můžeme spustit tento test. Pokud se jedná o první spuštění, musí engine stáhnout obrazy z docker.hub. To může chvíli trvat. Poté uvidíme, že se spustily dva kontejnery. Jeden je postgres a druhý je řadič Testcontainers. Tento druhý kontejner spravuje běžící kontejnery a i když se JVM nečekaně zastaví, pak vypne kontejnery a uklidí prostředí.
Shrňme si to
Zkušební kontejnery jsou velmi snadno použitelné nástroje, které nám pomáhají vytvářet integrační testy využívající kontejnery Docker. To nám dává větší flexibilitu a zvyšuje rychlost vývoje. Správné nastavení konfigurace testů zkracuje čas potřebný k nástupu nových vývojářů. Nemusí nastavovat všechny závislosti, stačí jim spustit napsané testy s vybranými konfiguračními soubory.