Testing av databaseinteraksjoner
Skriv integrasjonstester for datatilgangslag, og sørg for korrekt samhandling med relasjonsdatabaser.
Testing av databaseinteraksjoner er en gratis leksjon i Testing i praksis: JUnit, Mockito og integrasjonstester på CoddyKit. Dette er leksjon 3 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Testing i praksis: JUnit, Mockito og integrasjonstester, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Testing i praksis: JUnit, Mockito og integrasjonstester inneholder totalt 4 leksjoner.
Hvorfor teste databasesamhandling?
Databasesamhandling er en viktig del av de fleste applikasjoner. Testing av denne samhandlingen sikrer at appen lagrer, henter og oppdaterer data pålitelig.
- Dataintegritet: Garanterer at data lagres korrekt.
- Forretningslogikk: Verifiserer forretningsregler knyttet til data.
- Feilforebygging: Oppdager problemer før de når produksjonsmiljøet.
Utfordringene med ekte databaser
Direkte testing mot en ekte produksjonsdatabase kan være krevende og føre til upålitelige tester:
- Treg: Ekte databaser gjør testkjøringene betydelig lengre.
- Tilstandsfull: Tester kan etterlate data som påvirker påfølgende tester.
- Komplekst oppsett: Krever en kjørende ekstern tjeneste og bestemte konfigurasjoner.
- Isolasjon: Det er vanskelig å sikre at hver test kjører i et isolert miljø.
Databaser i minnet til unnsetning
For å overvinne disse utfordringene bruker vi ofte databaser i minnet til integrasjonstester. Dette er lette databaser som kjører fullstendig i minnet til applikasjonen.
- Raske: Ingen disk-IO og rask oppstart og avslutning.
- Isolerte: Hver testkjøring kan starte med en ny, tom database.
- Enkelt oppsett: Ofte trenger du bare en avhengighet og en tilkoblings-URL.
Populære alternativer er H2, HSQLDB og Apache Derby.
Konfigurer H2-databasen
La oss se hvor enkelt det er å koble til en H2-database i minnet ved hjelp av standard JDBC. Den fungerer akkurat som en vanlig database, men ligger i minnet til applikasjonen.
Du trenger H2-avhengigheten (for eksempel Maven: com.h2database:h2).
import java.sql.*;
public class H2Demo {
public static void main(String[] args) throws SQLException {
String jdbcUrl = "jdbc:h2:mem:testdb";
String username = "sa";
String password = "";
try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) {
Statement stmt = conn.createStatement();
stmt.execute("CREATE TABLE products (id INT PRIMARY KEY, name VARCHAR(255))");
stmt.executeUpdate("INSERT INTO products (id, name) VALUES (1, 'Laptop')");
ResultSet rs = stmt.executeQuery("SELECT * FROM products");
if (rs.next()) {
System.out.println("Product: " + rs.getString("name"));
}
}
}
}Introduksjon til datatilgangslaget
Applikasjonen din bruker vanligvis mønsteret Data Access Object (DAO) eller Repository for å samhandle med databasen. Dette laget abstraherer bort detaljer på lavt nivå i JDBC eller ORM.
Vi bruker en enkel ProductRepository til å håndtere Product-objekter.
public class Product {
private int id;
private String name;
public Product(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() { return id; }
public String getName() { return name; }
}
import java.util.List;
public interface ProductRepository {
void save(Product product);
Product findById(int id);
List<Product> findAll();
}Implementer et enkelt repository
Her er en grunnleggende implementasjon av ProductRepository ved hjelp av rå JDBC. I en virkelig applikasjon kan du bruke Spring sin JdbcTemplate eller en ORM som Hibernate.
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
// Product class and ProductRepository interface as defined previously
public class JdbcProductRepository implements ProductRepository {
private Connection conn;
public JdbcProductRepository(Connection conn) {
this.conn = conn;
try (Statement stmt = conn.createStatement()) {
stmt.execute("CREATE TABLE IF NOT EXISTS products (id INT PRIMARY KEY, name VARCHAR(255))");
} catch (SQLException e) { throw new RuntimeException(e); }
}
@Override
public void save(Product product) {
String sql = "INSERT INTO products (id, name) VALUES (?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, product.getId());
ps.setString(2, product.getName());
ps.executeUpdate();
} catch (SQLException e) { throw new RuntimeException(e); }
}
@Override
public Product findById(int id) {
String sql = "SELECT id, name FROM products WHERE id = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, id); ResultSet rs = ps.executeQuery();
if (rs.next()) { return new Product(rs.getInt("id"), rs.getString("name")); }
} catch (SQLException e) { throw new RuntimeException(e); }
return null;
}
@Override
public List<Product> findAll() {
List<Product> products = new ArrayList<>();
String sql = "SELECT id, name FROM products";
try (Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
products.add(new Product(rs.getInt("id"), rs.getString("name")));
}
} catch (SQLException e) { throw new RuntimeException(e); }
return products;
}
}Teste repositoryet – oppsett
La oss nå skrive et enkelt program for å teste JdbcProductRepository. Vi konfigurerer en H2-database i minnet spesielt for denne testkjøringen.
Legg merke til hvordan vi oppretter en ny databasetilkobling for repositoryet, slik at vi sikrer isolasjon.
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
// Product class (simplified for mobile)
class Product {
private int id; private String name;
public Product(int id, String name) { this.id = id; this.name = name; }
public int getId() { return id; }
public String getName() { return name; }
}
// ProductRepository interface
interface ProductRepository {
void save(Product product);
Product findById(int id);
List<Product> findAll();
}
// JdbcProductRepository (simplified for mobile)
class JdbcProductRepository implements ProductRepository {
private Connection conn;
public JdbcProductRepository(Connection conn) {
this.conn = conn;
try (Statement stmt = conn.createStatement()) {
stmt.execute("CREATE TABLE IF NOT EXISTS products (id INT PRIMARY KEY, name VARCHAR(255))");
} catch (SQLException e) { throw new RuntimeException(e); }
}
@Override public void save(Product product) {
String sql = "INSERT INTO products (id, name) VALUES (?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, product.getId()); ps.setString(2, product.getName());
ps.executeUpdate();
} catch (SQLException e) { throw new RuntimeException(e); }
}
@Override public Product findById(int id) { /* ... omitted ... */ return null; }
@Override public List<Product> findAll() { /* ... omitted ... */ return new ArrayList<>(); }
}
public class ProductRepositoryTestRunner {
public static void main(String[] args) throws SQLException {
String jdbcUrl = "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1";
try (Connection conn = DriverManager.getConnection(jdbcUrl, "sa", "")) {
System.out.println("DB connection established.");
ProductRepository repository = new JdbcProductRepository(conn);
Product laptop = new Product(1, "Laptop");
repository.save(laptop);
System.out.println("Saved product: " + laptop.getName());
Product foundProduct = repository.findById(1);
System.out.println("Found product: " + (foundProduct != null ? foundProduct.getName() : "None"));
List<Product> allProducts = repository.findAll();
System.out.println("Total products: " + allProducts.size());
}
}
}Kontroller databasetilstanden
I en virkelig test er det ikke nok å bare skrive ut resultatet. Du må assert at databasetilstanden samsvarer med forventningene dine etter en operasjon. Dette bekrefter at DAO-metodene fungerer korrekt.
La oss legge til noen grunnleggende kontroller for å verifisere resultatene.
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
// Product class (simplified for mobile)
class Product {
private int id; private String name;
public Product(int id, String name) { this.id = id; this.name = name; }
public int getId() { return id; }
public String getName() { return name; }
}
// ProductRepository interface
interface ProductRepository {
void save(Product product); Product findById(int id); List<Product> findAll();
}
// JdbcProductRepository (simplified for mobile)
class JdbcProductRepository implements ProductRepository {
private Connection conn;
public JdbcProductRepository(Connection conn) { /* ... omitted ... */ this.conn = conn; }
@Override public void save(Product product) { /* ... omitted ... */ }
@Override public Product findById(int id) {
String sql = "SELECT id, name FROM products WHERE id = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, id); ResultSet rs = ps.executeQuery();
if (rs.next()) { return new Product(rs.getInt("id"), rs.getString("name")); }
} catch (SQLException e) { throw new RuntimeException(e); }
return null;
}
@Override public List<Product> findAll() {
List<Product> products = new ArrayList<>();
String sql = "SELECT id, name FROM products";
try (Statement stmt = conn.createStatement()) {
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) { products.add(new Product(rs.getInt("id"), rs.getString("name"))); }
} catch (SQLException e) { throw new RuntimeException(e); }
return products;
}
}
public class ProductRepositoryAssertions {
public static void main(String[] args) throws SQLException {
String jdbcUrl = "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1";
try (Connection conn = DriverManager.getConnection(jdbcUrl, "sa", "")) {
ProductRepository repository = new JdbcProductRepository(conn);
Product laptop = new Product(1, "Laptop");
repository.save(laptop);
// Assertion 1: Check if product was saved
Product found = repository.findById(1);
if (found != null && found.getName().equals("Laptop")) {
System.out.println("Test Passed: Product saved & found.");
} else { System.out.println("Test Failed: Product not found or name mismatch."); }
// Assertion 2: Check total count
List<Product> all = repository.findAll();
if (all.size() == 1) {
System.out.println("Test Passed: Correct product count.");
} else { System.out.println("Test Failed: Incorrect product count."); }
}
}
}Sikre rene tester med transaksjoner
For mer robust testisolasjon, særlig når du bruker rammeverk som Spring, kan du benytte transaksjoner.
Ved å annotere testmetodene med @Transactional blir alle endringer som gjøres i databasen under testen, automatisk rullet tilbake når testen er ferdig. Dermed er databasen ren for neste test.
- Automatisk opprydding: Du trenger ikke manuelle
DELETE-setninger. - Isolasjon: Hver test kjører som om den var den eneste.
- Raskere: Tilbakerulling er ofte raskere enn å slette og sette inn data på nytt.
Beste praksis for databasetesting
Ta hensyn til disse tipsene for å gjøre databasens integrasjonstester effektive og enkle å vedlikeholde:
- Bruk databaser i minnet: For hastighet og isolasjon.
- Minimalt oppsett: Opprett bare tabeller og data som er nødvendige for den aktuelle testen.
- Transaksjonstester: Rull automatisk tilbake endringer.
- Tydelige assertions: Verifiser forventede data og tilstander.
- Fokuser på DAO: Test datatilgangslaget, ikke forretningslogikken her.
Kort sjekk: databasetesting
Hva er den viktigste fordelen ved å bruke en database i minnet, for eksempel H2, til integrasjonstester?
Oppsummering: testing av databasesamhandling
I denne leksjonen lærte vi hvordan vi effektivt tester databasesamhandling.
- Vi forsto utfordringene ved å teste med ekte databaser.
- Vi oppdaget databaser i minnet, som H2, som en rask og isolert løsning.
- Vi så hvordan vi kan konfigurere og samhandle med en H2-database programmatisk.
- Vi øvde på å skrive tester for et Data Access Object (DAO) for å sikre dataintegritet og korrekt oppførsel.
- Vi var innom transaksjonstester for automatisk opprydding.
Fortsett å øve for å sikre at datatilgangslaget i applikasjonen din er bunnsolid!
Lær deg Testing i praksis: JUnit, Mockito og integrasjonstester med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 12
- Leksjoner
- 48
Ofte stilte spørsmål
Er leksjonen «Testing av databaseinteraksjoner» gratis?
Ja – du kan lese valgfritt 3 av leksjonene i læringsstien Testing i praksis: JUnit, Mockito og integrasjonstester, inkludert «Testing av databaseinteraksjoner», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i Testing i praksis: JUnit, Mockito og integrasjonstester inneholder totalt 4 leksjoner.
Hva lærer jeg i «Testing av databaseinteraksjoner»?
Skriv integrasjonstester for datatilgangslag, og sørg for korrekt samhandling med relasjonsdatabaser. Du øver på Testing i praksis: JUnit, Mockito og integrasjonstester med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med Testing i praksis: JUnit, Mockito og integrasjonstester?
Ingen tidligere erfaring er nødvendig. Testing i praksis: JUnit, Mockito og integrasjonstester på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 3 av 4.
Hvor lang tid tar leksjonen «Testing av databaseinteraksjoner»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne Testing i praksis: JUnit, Mockito og integrasjonstester-leksjonen?
Ja. Alle Testing i praksis: JUnit, Mockito og integrasjonstester-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- Enhetstester kontra integrasjonstester
- Klargjøring av integrasjonstester
- Testing av databaseinteraksjoner
- Testing av eksterne API-er med WireMock