Test af databaseinteraktioner
Skriv integrationstests af datatilgangslag, og sørg for korrekt interaktion med relationelle databaser.
Test af databaseinteraktioner er en gratis Mestrer test: JUnit, Mockito og integrationstest-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Mestrer test: JUnit, Mockito og integrationstest, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Mestrer test: JUnit, Mockito og integrationstest-kurset indeholder 4 lektioner i alt.
Hvorfor teste databaseinteraktioner?
Databaseinteraktioner er en kritisk del af de fleste applikationer. Test af disse interaktioner sikrer, at din app gemmer, henter og opdaterer data pålideligt.
- Dataintegritet: Garanterer, at data gemmes korrekt.
- Forretningslogik: Verificerer forretningsregler, der vedrører data.
- Forebyggelse af fejl: Opdager problemer, før de når produktionen.
Udfordringerne ved rigtige databaser
Direkte test mod en rigtig produktionsdatabase kan være vanskelig og føre til upålidelige test:
- Langsom: Rigtige databaser tilføjer betydelig tid til testsamlinger.
- Tilstandsbaseret: Test kan efterlade data, som påvirker efterfølgende test.
- Kompleks opsætning: Kræver en kørende ekstern tjeneste og specifikke konfigurationer.
- Isolation: Det er vanskeligt at sikre, at hver test kører i et isoleret miljø.
Databaser i hukommelsen til undsætning
For at overvinde disse udfordringer bruger vi ofte databaser i hukommelsen til integrationstest. Det er lette databaser, der kører helt i applikationens hukommelse.
- Hurtige: Ingen disk-I/O og hurtig opstart og nedlukning.
- Isolerede: Hver testkørsel kan starte med en ny, tom database.
- Nem opsætning: Ofte kræves blot en afhængighed og en forbindelses-URL.
Populære valg omfatter H2, HSQLDB og Apache Derby.
Opsætning af din H2-database
Lad os se, hvor nemt det er at oprette forbindelse til en H2-database i hukommelsen ved hjælp af standard-JDBC. Den fungerer ligesom en almindelig database, men ligger i applikationens hukommelse.
Du skal bruge H2-afhængigheden (f.eks. 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"));
}
}
}
}Introduktion til dataadgangslaget
Din applikation bruger typisk mønsteret Data Access Object (DAO) eller Repository til at interagere med databasen. Dette lag skjuler detaljerne på lavt niveau i JDBC eller ORM.
Vi bruger en simpel ProductRepository til at administrere 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();
}Implementering af et simpelt Repository
Her er en grundlæggende implementering af vores ProductRepository ved hjælp af rå JDBC. I en rigtig applikation kunne du bruge Spring's 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;
}
}Test af Repository – opsætning
Lad os nu skrive et simpelt program til at teste vores JdbcProductRepository. Vi konfigurerer en H2-database i hukommelsen specifikt til denne testkørsel.
Bemærk, hvordan vi opretter en ny databaseforbindelse til vores Repository, så isolation sikres.
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());
}
}
}Kontrol af databasetilstanden
I en rigtig test er det ikke nok bare at udskrive resultatet. Du skal kontrollere, at databasetilstanden svarer til dine forventninger efter en handling. Det bekræfter, at dine DAO-metoder fungerer korrekt.
Lad os tilføje nogle grundlæggende kontroller for at verificere resultaterne.
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."); }
}
}
}Sikring af rene test med transaktioner
For at opnå mere robust testisolering, især når du bruger rammeværk som Spring, kan du benytte transaktioner.
Ved at annotere dine testmetoder med @Transactional bliver alle ændringer i databasen inden for testen automatisk rullet tilbage, når testen er færdig. Det efterlader databasen i en ren tilstand til den næste test.
- Automatisk oprydning: Ingen behov for manuelle
DELETE-sætninger. - Isolation: Hver test kører, som om den var den eneste.
- Hurtigere: Tilbagerulning er ofte hurtigere end at slette og indsætte data igen.
Bedste praksis for databasetest
Overvej disse råd for at gøre dine databaseintegrationstest effektive og nemme at vedligeholde:
- Brug databaser i hukommelsen: For at opnå hastighed og isolation.
- Minimal opsætning: Opret kun de tabeller og data, der er nødvendige for den specifikke test.
- Transaktionelle test: Rul automatisk ændringer tilbage.
- Tydelige kontroller: Verificer de forventede data og den forventede tilstand.
- Fokusér på DAO: Test dataadgangslaget, ikke forretningslogikken her.
Hurtigt tjek: Databasetest
Hvilken af følgende er en primær fordel ved at bruge en database i hukommelsen som H2 til integrationstest?
Opsummering: Test af databaseinteraktioner
I denne lektion lærte vi, hvordan man effektivt tester databaseinteraktioner.
- Vi forstod udfordringerne ved at teste med rigtige databaser.
- Vi opdagede databaser i hukommelsen som H2 som en hurtig og isoleret løsning.
- Vi så, hvordan man programmæssigt opsætter og interagerer med en H2-database.
- Vi øvede os i at skrive test til et Data Access Object (DAO) for at sikre dataintegritet og korrekt funktion.
- Vi stiftede bekendtskab med transaktionelle test til automatisk oprydning.
Bliv ved med at øve dig, så din applikations datalag bliver helt robust!
Lær Mestrer test: JUnit, Mockito og integrationstest med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 12
- Lektioner
- 48
Ofte stillede spørgsmål
Er lektionen “Test af databaseinteraktioner” gratis?
Ja — alle 3 lektioner i læringssporet Mestrer test: JUnit, Mockito og integrationstest, inklusive “Test af databaseinteraktioner”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Mestrer test: JUnit, Mockito og integrationstest-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Test af databaseinteraktioner”?
Skriv integrationstests af datatilgangslag, og sørg for korrekt interaktion med relationelle databaser. Du øver dig i Mestrer test: JUnit, Mockito og integrationstest med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Mestrer test: JUnit, Mockito og integrationstest?
Der kræves ingen tidligere erfaring. Mestrer test: JUnit, Mockito og integrationstest på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.
Hvor lang tid tager lektionen “Test af databaseinteraktioner”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Mestrer test: JUnit, Mockito og integrationstest-lektion?
Ja. Alle Mestrer test: JUnit, Mockito og integrationstest-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Unit-tests kontra integrationstests
- Opsætning af integrationstests
- Test af databaseinteraktioner
- Test af eksterne API'er med WireMock