Testauksen hallinta: JUnit, Mockito ja integraatiotestit · Oppitunti

Tietokantavuorovaikutusten testaaminen

Kirjoittakaa integraatiotestejä tietojen käyttökerroksille ja varmistakaa oikea vuorovaikutus relaatiotietokantojen kanssa.

Oppitunti 3/412 vaihetta

Tietokantavuorovaikutusten testaaminen on ilmainen Testauksen hallinta: JUnit, Mockito ja integraatiotestit-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu Testauksen hallinta: JUnit, Mockito ja integraatiotestit-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Testauksen hallinta: JUnit, Mockito ja integraatiotestit-kurssilla on yhteensä 4 oppituntia.

Miksi tietokantavuorovaikutusta testataan?

Tietokantavuorovaikutus on useimpien sovellusten keskeinen osa. Sen testaaminen varmistaa, että sovellus tallentaa, hakee ja päivittää tietoja luotettavasti.

  • Tietojen eheys: Varmistaa, että tiedot tallennetaan oikein.
  • Liiketoimintalogiikka: Varmistaa tietoihin liittyvien liiketoimintasääntöjen toiminnan.
  • Virheiden ehkäisy: Havaitsee ongelmat ennen kuin ne päätyvät tuotantoon.

Tuotantotietokantojen haasteet

Suoraan todellisen tuotantotietokannan kanssa testaaminen voi olla hankalaa ja johtaa epäluotettaviin testeihin:

  • Hitaus: Todelliset tietokannat pidentävät testikokonaisuuksien suoritusaikaa merkittävästi.
  • Tila: Testit voivat jättää jälkeensä tietoja, jotka vaikuttavat seuraaviin testeihin.
  • Monimutkainen määritys: Edellyttää käynnissä olevaa ulkoista palvelua ja tiettyjä määrityksiä.
  • Eristäminen: Jokaisen testin suorittamista eristetyssä ympäristössä on vaikea varmistaa.

Muistinsisäiset tietokannat apuun

Näiden haasteiden ratkaisemiseksi integraatiotesteissä käytetään usein muistinsisäisiä tietokantoja. Ne ovat kevyitä tietokantoja, jotka toimivat kokonaan sovelluksen muistissa.

  • Nopeus: Levyn I/O-operaatioita ei tarvita, joten käynnistys ja sammutus ovat nopeita.
  • Eristys: Jokainen testiajo voi alkaa uudesta ja tyhjästä tietokannasta.
  • Helppo määritys: Usein tarvitaan vain riippuvuus ja yhteyden URL-osoite.

Suosittuja vaihtoehtoja ovat H2, HSQLDB ja Apache Derby.

H2-tietokannan määrittäminen

Katsotaan, kuinka helppoa H2-muistinsisäiseen tietokantaan yhdistäminen on tavallista JDBC:tä käyttämällä. Se toimii aivan tavallisen tietokannan tavoin, mutta sijaitsee sovelluksen muistissa.

Tarvitsette H2-riippuvuuden, esimerkiksi Mavenissa: 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"));
      }
    }
  }
}

Tietojen käyttökerroksen esittely

Sovelluksessanne käytetään yleensä Data Access Object (DAO)- tai Repository-suunnittelumallia tietokannan käsittelyyn. Tämä kerros piilottaa matalan tason JDBC- tai ORM-yksityiskohdat.

Käytämme yksinkertaista ProductRepository-komponenttia Product-olioiden hallintaan.

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();
}

Yksinkertaisen repositoryn toteuttaminen

Tässä on ProductRepository-komponentin perusimplementaatio raakaa JDBC:tä käyttäen. Todellisessa sovelluksessa voitaisiin käyttää Springin JdbcTemplate-luokkaa tai Hibernate-tyyppistä ORM:ää.

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;
  }
}

Repositoryn testaaminen – määritys

Kirjoitetaan nyt yksinkertainen ohjelma JdbcProductRepository-komponentin testaamiseen. Määritämme H2-muistinsisäisen tietokannan juuri tätä testiajoa varten.

Huomatkaa, että luomme repositorylle uuden tietokantayhteyden, mikä varmistaa eristyksen.

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());
    }
  }
}

Tietokannan tilan tarkistaminen

Oikeassa testissä pelkkä tulostaminen ei riitä. Teidän on väitettävä, että tietokannan tila vastaa odotuksia toiminnon suorittamisen jälkeen. Näin varmistetaan DAO-metodien oikea toiminta.

Lisätään muutama perus tarkistus tulosten varmistamiseksi.

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."); }
    }
  }
}

Puhtaiden testien varmistaminen transaktioilla

Testien eristyksen parantamiseksi, erityisesti Springin kaltaisia kehyksiä käytettäessä, voitte hyödyntää transaktioita.

Kun merkitsette testimetodit @Transactional-annotaatiolla, kaikki testin aikana tietokantaan tehdyt muutokset perutaan automaattisesti testin päätyttyä. Näin tietokanta jää puhtaaseen tilaan seuraavaa testiä varten.

  • Automaattinen siivous: Manuaalisia DELETE-lauseita ei tarvita.
  • Eristys: Jokainen testi suoritetaan ikään kuin se olisi ainoa testi.
  • Nopeus: Muutosten peruminen on usein nopeampaa kuin tietojen poistaminen ja lisääminen uudelleen.

Tietokantatestauksen parhaat käytännöt

Jotta tietokannan integraatiotestit olisivat tehokkaita ja helposti ylläpidettäviä, huomioikaa seuraavat vinkit:

  • Käyttäkää muistinsisäisiä tietokantoja: Nopeuden ja eristyksen vuoksi.
  • Pitäkää määritys suppeana: Luokaa vain kyseisessä testissä tarvittavat taulut ja tiedot.
  • Käyttäkää transaktiollisia testejä: Perukaa muutokset automaattisesti.
  • Tehkää selkeät väitteet: Varmistakaa odotetut tiedot ja tila.
  • Keskittykää DAO-kerrokseen: Testatkaa tässä tietojen käyttökerrosta, älkää liiketoimintalogiikkaa.

Pikatarkistus: tietokantatestaus

Mikä seuraavista on H2:n kaltaisen muistinsisäisen tietokannan käytön ensisijainen hyöty integraatiotesteissä?

Kertaus: tietokantavuorovaikutuksen testaaminen

Tässä oppitunnissa opimme testaamaan tietokantavuorovaikutusta tehokkaasti.

  • Ymmärsimme todellisten tietokantojen testaamisen haasteet.
  • Tutustuimme H2:n kaltaisiin muistinsisäisiin tietokantoihin nopeana ja eristettynä ratkaisuna.
  • Näimme, miten H2-tietokanta määritetään ja sitä käytetään ohjelmallisesti.
  • Harjoittelimme Data Access Object (DAO)-komponentin testien kirjoittamista ja tietojen eheyden sekä oikean toiminnan varmistamista.
  • Tutustuimme transaktionaalisiin testeihin automaattista siivousta varten.

Jatkakaa harjoittelua, jotta sovelluksenne tietokerros pysyy vankkana!

Aloita maksutta

Opi Testauksen hallinta: JUnit, Mockito ja integraatiotestit tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
12
Oppitunnit
48

Usein kysytyt kysymykset

Onko oppitunti ”Tietokantavuorovaikutusten testaaminen” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa Testauksen hallinta: JUnit, Mockito ja integraatiotestit-oppimispolun 3 oppituntia, myös oppitunnin “Tietokantavuorovaikutusten testaaminen”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Testauksen hallinta: JUnit, Mockito ja integraatiotestit-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Tietokantavuorovaikutusten testaaminen”?

Kirjoittakaa integraatiotestejä tietojen käyttökerroksille ja varmistakaa oikea vuorovaikutus relaatiotietokantojen kanssa. Harjoittelet Testauksen hallinta: JUnit, Mockito ja integraatiotestit-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Testauksen hallinta: JUnit, Mockito ja integraatiotestit-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Testauksen hallinta: JUnit, Mockito ja integraatiotestit-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.

Kuinka kauan ”Tietokantavuorovaikutusten testaaminen”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Testauksen hallinta: JUnit, Mockito ja integraatiotestit-oppitunnilla?

Kyllä. Jokainen Testauksen hallinta: JUnit, Mockito ja integraatiotestit-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Yksikkö- ja integraatiotestit
  2. Integraatiotestien määrittäminen
  3. Tietokantavuorovaikutusten testaaminen
  4. Ulkoisten rajapintojen testaaminen WireMockilla
← Takaisin: Testauksen hallinta: JUnit, Mockito ja integraatiotestit