Introduktion til DBUnit

1. Introduktion

I denne tutorial tager vi et kig på DBUnit, et enhedstestværktøj, der bruges til prøverelationel database interaktion i Java.

Vi ser, hvordan det hjælper os med at få vores database til en kendt tilstand og hævde mod en forventet tilstand.

2. Afhængigheder

Først kan vi føje DBUnit til vores projekt fra Maven Central ved at tilføje dbunit afhængighed af vores pom.xml:

 org.dbunit dbunit 2.7.0 test 

Vi kan slå op på den seneste version på Maven Central.

3. Hej verdenseksempel

Lad os derefter definere en databaseskema:

skema.sql:

Opret TABEL, HVIS IKKE FINDER KLIENTER (`id` int AUTO_INCREMENT NOT NULL,` first_name` varchar (100) NOT NULL, `last_name` varchar (100) NOT NULL, PRIMARY KEY (` id`)); Opret TABEL, HVIS IKKE FINDER VARER (`id` int AUTO_INCREMENT NOT NULL,` title` varchar (100) NOT NULL, `produced` date,` price` float, PRIMARY KEY (`id`)); 

3.1. Definition af den oprindelige databaseindhold

DBUnit lader os definere og indlæse vores testdatasæt på en enkel mådeerklærende måde.

Vi definerer hver tabelrække med et XML-element, hvor tagnavnet er et tabelnavn, og attributnavne og -værdier kortlægges til henholdsvis kolonnenavne og værdier. Rækkedataene kan oprettes til flere tabeller. Vi er nødt til at gennemføre getDataSet () metode til DataSourceBasedDBTestCase for at definere det oprindelige datasæt, hvor vi kan bruge FlatXmlDataSetBuilder at henvise til vores XML-fil:

data.xml:

3.2. Initialisering af databaseforbindelse og skema

Nu hvor vi har vores skema, skal vi initialisere vores database.

Vi er nødt til at udvide DataSourceBasedDBTestCase klasse og initialisere databaseskemaet i dets getDataSource () metode:

DataSourceDBUnitTest.java:

offentlig klasse DataSourceDBUnitTest udvider DataSourceBasedDBTestCase {@Override beskyttet DataSource getDataSource () {JdbcDataSource dataSource = ny JdbcDataSource (); dataSource.setURL ("jdbc: h2: mem: standard; DB_CLOSE_DELAY = -1; init = kører fra 'classpath: schema.sql'"); dataSource.setUser ("sa"); dataSource.setPassword ("sa"); returnere datakilde; } @ Override beskyttet IDataSet getDataSet () kaster undtagelse {returner ny FlatXmlDataSetBuilder (). Build (getClass (). GetClassLoader () .getResourceAsStream ("data.xml")); }}

Her sendte vi en SQL-fil til en H2-hukommelsesdatabase i dens forbindelsesstreng. Hvis vi vil teste på andre databaser, skal vi give vores brugerdefinerede implementering til det.

Husk det, i vores eksempel DBUnit geninitialiserer databasen med de givne testdata før hver testmetodeudførelse.

Der er flere måder at konfigurere dette via SetUpOperation og TearDownOperation:

@ Override beskyttet DatabaseOperation getSetUpOperation () {return DatabaseOperation.REFRESH; } @ Override beskyttet DatabaseOperation getTearDownOperation () {return DatabaseOperation.DELETE_ALL; }

Det OPDATER operation, beder DBUnit om at opdatere alle sine data. Dette vil sikre, at alle caches ryddes op, og vores enhedstest får ingen indflydelse fra en anden enhedstest. Det SLET ALT betjening sikrer, at alle data fjernes i slutningen af ​​hver enhedstest. I vores tilfælde fortæller vi DBUnit, at under opsætningen ved hjælp af getSetUpOperation metodeimplementering opdaterer vi alle cacher. Endelig beder vi DBUnit om at fjerne alle data under nedrivningsoperationen ved hjælp af getTearDownOperation metodeimplementering.

3.3. Sammenligning af den forventede stat og den faktiske stat

Lad os nu undersøge vores faktiske testsag. Til denne første test holder vi det simpelt - vi indlæser vores forventede datasæt og sammenligner det med datasættet hentet fra vores DB-forbindelse:

@Test offentlig ugyldighed givenDataSetEmptySchema_whenDataSetCreated_thenTablesAreEqual () kaster undtagelse {IDataSet expectDataSet = getDataSet (); ITable expectTable = expectDataSet.getTable ("CLIENTS"); IDataSet databaseDataSet = getConnection (). CreateDataSet (); ITable actualTable = databaseDataSet.getTable ("KLIENTER"); assertEquals (forventet tabel, faktisk tabel); }

4. Dyb dykk ind i Påstande

I det forrige afsnit så vi et grundlæggende eksempel på at sammenligne det faktiske indhold i en tabel med et forventet datasæt. Nu skal vi opdage DBUnits support til tilpasning af datahevdinger.

4.1. Påstå med en SQL-forespørgsel

En ligetil måde at kontrollere den aktuelle tilstand er med en SQL-forespørgsel.

I dette eksempel indsætter vi en ny post i CLIENTS-tabellen og kontrollerer derefter indholdet af den nyoprettede række. Vi definerede den forventede output i en separat XML-fil, og ekstraherede den aktuelle rækkeværdi med en SQL-forespørgsel:

@Test offentlig ugyldighed givenDataSet_whenInsert_thenTableHasNewClient () kaster undtagelse {prøv (InputStream er = getClass (). GetClassLoader (). GetResourceAsStream ("dbunit / forventet-bruger.xml")) {IDataSet forventetDataSet = ny FlatXmlData.) ITable expectTable = expectDataSet.getTable ("CLIENTS"); Forbindelse conn = getDataSource (). GetConnection (); conn.createStatement () .executeUpdate ("INDSÆT I KLIENTER (fornavn, efternavn) VÆRDIER ('John', 'Jansen')"); ITable actualData = getConnection () .createQueryTable ("result_name", "SELECT * FROM CLIENTS WHERE last_name =" Jansen ""); assertEqualsIgnoreCols (expectTable, actualData, new String [] {"id"}); }}

Det getConnection () metode til DBTestCase stamfar klasse returnerer en DBUnit-specifik repræsentation af datakildeforbindelsen (en IDatabaseConnection eksempel). Det createQueryTable () metode til IDatabaseConnection kan bruges til at hente faktiske data fra databasen, til sammenligning med den forventede databasetilstand ved hjælp af Assertion.assertEquals () metode. SQL-forespørgslen blev videregivet createQueryTable () er den forespørgsel, vi vil teste. Det returnerer en Bord eksempel, som vi bruger til at gøre vores påstand.

4.2. Ignorer kolonner

Nogle gange i databasetest vil vi ignorere nogle kolonner i de faktiske tabeller. Disse er normalt automatisk genererede værdier, som vi ikke kan kontrollere nøjagtigt genererede primære nøgler eller aktuelle tidsstempler.

Vi kunne gøre dette ved at udelade kolonnerne fra SELECT-klausulerne i SQL-forespørgslerne, men DBUnit giver et mere praktisk værktøj til at opnå dette. Med de statiske metoder i DefaultColumnFilter klasse kan vi skabe et nyt ITable forekomst fra en eksisterende ved at ekskludere nogle af kolonnernesom vist her:

@Test offentlig ugyldighed givenDataSet_whenInsert_thenGetResultsAreStillEqualIfIgnoringColumnsWithDifferentProduced () kaster Undtagelse {Connection connection = tester.getConnection (). GetConnection (); Streng [] excl. Kolonner = {"id", "produceret"}; prøv (InputStream er = getClass (). getClassLoader () .getResourceAsStream ("dbunit / forventet-ignorering-registreret_at.xml")) {IDataSet forventetDataSet = ny FlatXmlDataSetBuilder (). build (er); ITable expectTable = ekskluderetColumnTable (forventetDataSet.getTable ("ITEMS"), ekskluderetKolonner); connection.createStatement () .executeUpdate ("INDSÆT I PUNKTER (titel, pris, produceret) VÆRDIER ('Halskæde', 199,99, nu ())"); IDataSet databaseDataSet = tester.getConnection (). CreateDataSet (); ITable actualTable = ekskluderetKolonnerTabel (databaseDataSet.getTable ("ITEMS"), ekskluderetKolonner); assertEquals (forventet tabel, faktisk tabel); }}

4.3. Undersøgelse af flere fejl

Hvis DBUnit finder en forkert værdi, så kaster den straks en Påstand Fejl.

I specifikke tilfælde kan vi bruge DiffCollectingFailureHandler klasse, som vi kan videregive til Assertion.assertEquals () metode som et tredje argument.

Denne fejlhåndterer samler alle fejl i stedet for at stoppe ved den første, hvilket betyder det det Assertion.assertEquals () metoden vil altid lykkes, hvis vi bruger DiffCollectingFailureHandler. Derfor bliver vi nødt til at programmatisk kontrollere, om handleren fandt fejl:

@Test offentlig ugyldighed givenDataSet_whenInsertUnexpectedData_thenFailOnAllUnexpectedValues ​​() kaster undtagelse {prøv (InputStream er = getClass (). GetClassLoader () .getResourceAsStream ("dbunit / forventet-multiple-failures.xmlSetData) (Data = forventet)) (Data = forventet-mangler. ); ITable expectTable = expectDataSet.getTable ("ITEMS"); Forbindelse conn = getDataSource (). GetConnection (); DiffCollectingFailureHandler collectorHandler = ny DiffCollectingFailureHandler (); conn.createStatement () .executeUpdate ("INSERT IN TO ITEMS (title, price) VALUES ('Battery', '1000000')"); ITable actualData = getConnection (). CreateDataSet (). GetTable ("ITEMS"); assertEquals (forventet tabel, faktisk data, indsamlingshandler); if (! collectionHandler.getDiffList (). isEmpty ()) {String message = (String) collectHandler.getDiffList () .stream () .map (d -> formatDifference ((Difference) d)) .collect (joining ("\ n ")); logger.error (() -> besked); }}} privat statisk strengformatDifference (forskel diff) {returnerer "forventet værdi i" + diff.getExpectedTable () .getTableMetaData () .getTableName () + "." + diff.getColumnName () + "række" + diff.getRowIndex () + ":" + diff.getExpectedValue () + ", men var:" + diff.getActualValue (); }

Derudover leverer handler fejlene i form af Forskel forekomster, som lader os formatere fejlene.

Efter at have kørt testen får vi en formateret rapport:

java.lang.AssertionError: forventet værdi i ITEMS.pris række 5: 199.99, men var: 1000000.0 forventet værdi i ITEMS. produceret række 5: 2019-03-23, men var: null forventet værdi i ITEMS. titel række 5: Halskæde , men var: Batteri på com.baeldung.dbunit.DataSourceDBUnitTest.givenDataSet_whenInsertUnexpectedData_thenFailOnAllUnexpectedValues ​​(DataSourceDBUnitTest.java:91)

Det er vigtigt at bemærke, at vi på dette tidspunkt forventede, at den nye vare ville have en pris på 199,99, men den var 1000000,0. Så ser vi, at produktionsdatoen var 2019-03-23, men i sidste ende var den nul. Endelig var den forventede vare en halskæde, og i stedet fik vi et batteri.

5. Konklusion

I denne artikel så vi, hvordan DBUnit leverer en erklærende måde at definere testdata på til test dataadgangslag af Java-applikationer.

Som altid er den fulde kildekode til eksemplerne tilgængelig på GitHub.