JavaRush /Java-Blog /Random-DE /Integrationstest einer Datenbank mit MariaDB als Ersatz f...

Integrationstest einer Datenbank mit MariaDB als Ersatz für MySql

Veröffentlicht in der Gruppe Random-DE
Integrationstest einer Datenbank mit MariaDB als Ersatz für MySql - 1Heute möchte ich über das Testen sprechen, denn je mehr der Code mit Tests abgedeckt wird, desto besser und zuverlässiger gilt er. Sprechen wir nicht über Unit-Tests, sondern über Integrationstests von Datenbanken. Was genau ist der Unterschied zwischen Unit-Tests und Integrationstests? Integrationstest einer Datenbank mit MariaDB als Ersatz für MySql - 2Modular (Einheit) ist das Testen eines Programms auf der Ebene einzelner Module, Methoden oder Klassen, d. h. Tests sind schnell und einfach und wirken sich auf die am stärksten teilbaren Teile der Funktionalität aus. Sie werden auch als „ein Test pro Methode“ bezeichnet. Integrationsmodule sind langsamer und schwerer und können aus mehreren Modulen und zusätzlicher Funktionalität bestehen. Integrationstest einer Datenbank mit MariaDB als Ersatz für MySql - 3Warum sind Tests für die Dao-Schicht (Data Access Object) Integrationstests? Denn um Methoden mit Abfragen an die Datenbank zu testen, müssen wir eine separate Datenbank im RAM erstellen und die Hauptdatenbank ersetzen. Die Idee ist, dass wir die benötigten Tabellen erstellen, sie mit Testdaten füllen und die Korrektheit der Methoden der Repository-Klasse überprüfen (schließlich wissen wir, wie das Endergebnis in einem bestimmten Fall aussehen sollte). Also, fangen wir an. Themen zur Anbindung einer Datenbank sind schon lange weit verbreitet, daher möchte ich heute nicht weiter darauf eingehen, sondern nur die Teile des Programms betrachten, die uns interessieren. Standardmäßig gehen wir davon aus, dass unsere Anwendung auf Spring Boot basiert. Für die Spring JDBC-Dao-Schicht (zur besseren Übersichtlichkeit) ist unsere Hauptdatenbank MySQL und wir werden sie durch MariaDB ersetzen (sie sind maximal kompatibel). Dementsprechend wird es bei MySQL-Skripten niemals zu Konflikten mit dem MariaDB-Dialekt kommen, wie es bei H2 der Fall sein wird. Wir gehen außerdem bedingt davon aus, dass unser Programm Liquibase verwendet, um Änderungen am Datenbankschema zu verwalten und anzuwenden, und dass dementsprechend alle angewendeten Skripte in unserer Anwendung gespeichert werden.

Projektstruktur

Es werden nur die betroffenen Teile angezeigt: Integrationstest einer Datenbank mit MariaDB als Ersatz für MySql - 5Und ja, heute werden wir Roboter erstellen)) Integrationstest einer Datenbank mit MariaDB als Ersatz für MySql - 6Skript für die Tabelle, deren Methoden wir heute testen werden (create_table_robots.sql):
CREATE TABLE `robots`
(
   `id`   BIGINT(20) NOT NULL AUTO_INCREMENT,
   `name` CHAR(255) CHARACTER SET utf8 NOT NULL,
   `cpu`  CHAR(255) CHARACTER SET utf8 NOT NULL,
   `producer`  CHAR(255) CHARACTER SET utf8 NOT NULL,
   PRIMARY KEY (`id`)
) ENGINE = InnoDB
 DEFAULT CHARSET = utf8;
Die Entität, die diese Tabelle darstellt:
@Builder
@Data
public class Robot {

   private Long id;

   private String name;

   private String cpu;

   private String producer;
}
Schnittstelle für das getestete Repository:
public interface RobotDAO {

   Robot findById(Long id);

   Robot create(Robot robot);

   List<Robot> findAll();

   Robot update(Robot robot);

   void delete(Long id);
}
Tatsächlich handelt es sich hier um Standard-CRUD-Operationen ohne Exoten, daher werden wir die Implementierung nicht aller Methoden in Betracht ziehen (naja, das wird niemanden überraschen), sondern einiger – der Kürze halber:
@Repository
@AllArgsConstructor
public class RobotDAOImpl implements RobotDAO {

   private static final String FIND_BY_ID = "SELECT id, name, cpu, producer FROM robots WHERE id = ?";

   private static final String UPDATE_BY_ID = "UPDATE robots SET name = ?, cpu = ?, producer = ?  WHERE id = ?";

   @Autowired
   private final JdbcTemplate jdbcTemplate;

   @Override
   public Robot findById(Long id) {
       return jdbcTemplate.queryForObject(FIND_BY_ID, robotMapper(), id);
   }

   @Override
   public Robot update(Robot robot) {
       jdbcTemplate.update(UPDATE_BY_ID,
               robot.getName(),
               robot.getCpu(),
               robot.getProducer(),
               robot.getId());

       return robot;
   }

   private RowMapper<Robot> robotMapper() {
       return (rs, rowNum) ->
               Robot.builder()
                       .id(rs.getLong("id"))
                       .name(rs.getString("name"))
                       .cpu(rs.getString("cpu"))
                       .producer(rs.getString("producer"))
                       .build();
   }
Lassen Sie uns einen kleinen Exkurs machen und sehen, was mit unseren Abhängigkeiten los ist (es werden nur diejenigen vorgestellt, die für den demonstrierten Teil der Anwendung verwendet wurden):
<dependencies>
   <dependency>
       <groupId>org.mariadb.jdbc</groupId>
       <artifactId>mariadb-java-client</artifactId>
       <version>2.5.2</version>
       <scope>test</scope>
   </dependency>
   <dependency>
       <groupId>org.craftercms.mariaDB4j</groupId>
       <artifactId>mariaDB4j-springboot</artifactId>
       <version>2.4.2.3</version>
       <scope>test</scope>
   </dependency>
   <dependency>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
       <version>1.18.10</version>
       <scope>provided</scope>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-test</artifactId>
       <version>2.2.1.RELEASE</version>
       <scope>test</scope>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-jdbc</artifactId>
       <version>2.2.1.RELEASE</version>
   </dependency>
</dependencies>
4 – Abhängigkeit für die MariaDb-Datenbank selbst 10 – Abhängigkeit für die Verbindung mit SpringBoot 16 – Lombok (naja, ich denke, jeder weiß, was für eine Art von Bibliothek das ist) 22 – Starter für Tests (wo die von uns benötigte JUnit eingebettet ist) 28 – Starter für Arbeiten mit springJdbc Werfen wir einen Blick auf den Spring-Container mit den für unsere Tests benötigten Beans (insbesondere der MariaDB-Erstellungs-Bean):
@Configuration
public class TestConfigDB {

   @Bean
   public MariaDB4jSpringService mariaDB4jSpringService() {
       return new MariaDB4jSpringService();
   }

   @Bean
   public DataSource dataSource(MariaDB4jSpringService mariaDB4jSpringService) {
       try {
           mariaDB4jSpringService.getDB().createDB("testDB");
       } catch (ManagedProcessException e) {
         e.printStackTrace();
       }

       DBConfigurationBuilder config = mariaDB4jSpringService.getConfiguration();

       return DataSourceBuilder
               .create()
               .username("root")
               .password("root")
               .url(config.getURL("testDB"))
               .driverClassName("org.mariadb.jdbc.Driver")
               .build();
   }

   @Bean
   public JdbcTemplate jdbcTemplate(DataSource dataSource) {
       return new JdbcTemplate(dataSource);
   }
}
5 – die Hauptkomponente zum Erhöhen von MariaDB (für Anwendungen, die auf dem Spring Framework basieren) 10 – Definieren einer Datenbank-Bean 12 – Festlegen des Namens der erstellten Datenbank 17 – Herausziehen der Konfigurationen für unseren Fall 19 – Erstellen einer Datenbank mithilfe des Builder-Musters ( ein guter Überblick über das Muster ) Und schließlich geht es bei der ganzen Aufregung um die JdbcTemplate-Bean für die Kommunikation mit der Datenbank, die erstellt wird. Die Idee ist, dass wir eine Hauptklasse für Tao-Tests haben werden, von der alle Tao-Testklassen erben werden, zu deren Aufgaben gehören:
  1. Starten einiger Skripte, die in der Hauptdatenbank verwendet werden (Skripte zum Erstellen von Tabellen, Ändern von Spalten usw.);
  2. Starten von Testskripten, die Tabellen mit Testdaten füllen;
  3. Tabellen löschen.
@SpringBootTest(classes = TestConfigDB.class)
public abstract class DataBaseIT {

   @Autowired
   private JdbcTemplate jdbcTemplate;

   public JdbcTemplate getJdbcTemplate() {
       return jdbcTemplate;
   }

   public void fillDataBase(String[] initList) {
       for (String x : initList) {
           try {
               jdbcTemplate.update(IOUtils.resourceToString("/db.migrations/" + x, StandardCharsets.UTF_8));
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }

   public void cleanDataBase() {
       getJdbcTemplate().update("DROP database testDB");
       getJdbcTemplate().update("CREATE database testDB");
       getJdbcTemplate().update("USE testDB");
   }

   public void fillTables(String[] fillList) {
       for (String x : fillList) {
           try {
               Stream.of(
                       IOUtils.resourceToString("/fill_scripts/" + x, StandardCharsets.UTF_8))
                       .forEach(jdbcTemplate::update);
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }
}
1 - Mithilfe der Annotation @SpringBootTest legen wir eine Testkonfiguration fest. 11 - Als Argument in dieser Methode übergeben wir die Namen der Tabellen, die wir benötigen, und er wird sie als verantwortungsbewusster harter Arbeiter für uns laden (was uns die Möglichkeit gibt). diese Methode so oft wiederzuverwenden, wie unser Herz es wünscht) 21 – Wir verwenden diese Methode zum Bereinigen, nämlich zum Löschen aller Tabellen (und ihrer Daten) aus der Datenbank. 27 – Das Argument in dieser Methode ist ein Array von Namen von Skripten mit Testdaten das zum Testen einer bestimmten Methode geladen wird. Unser Skript mit Testdaten:
INSERT INTO robots(name, cpu, producer)
VALUES ('Rex', 'Intel Core i5-9400F', 'Vietnam'),
      ('Molly', 'AMD Ryzen 7 2700X', 'China'),
      ('Ross', 'Intel Core i7-9700K', 'Malaysia')
Und nun, wofür wir uns alle heute versammelt haben.

Tao-Testkurs

@RunWith(SpringRunner.class)
public class RobotDataBaseIT extends DataBaseIT {

   private static RobotDAO countryDAO;

   @Before
   public void fillData() {
       fillDataBase(new String[]{
               "create_table_robots.sql"
       });
       countryDAO = new RobotDAOImpl(getJdbcTemplate());
   }

   @After
   public void clean() {
       cleanDataBase();
   }

   private RowMapper<Robot> robotMapper() {
       return (rs, rowNum) ->
               Robot.builder()
                       .id(rs.getLong("id"))
                       .name(rs.getString("name"))
                       .cpu(rs.getString("cpu"))
                       .producer(rs.getString("producer"))
                       .build();
   }
2 – Wir erben von der Hauptklasse für unsere Tests. 4 – Unser getestetes Repository. 7 – Eine Methode, die vor jedem Test gestartet wird. 8 – Wir verwenden die Methode der übergeordneten Klasse, um die erforderlichen Tabellen zu laden. 11 – Wir initialisieren unser Dao. 15 – Eine Methode Das wird nach jedem Test gestartet und bereinigt unsere Datenbank. 19 – Implementierung unseres RowMappers, analog zur Tao-Klasse. Wir verwenden @Before und @After, die vor und nach einer Testmethode verwendet werden, aber wir könnten eine Bibliothek verwenden, die uns dies ermöglicht um Anmerkungen zu verwenden, die an den Beginn und das Ende von Ausführungstests dieser Klasse gebunden sind. Zum Beispiel dieses , was die Tests erheblich beschleunigen würde, da Tabellen jedes Mal und einmal pro Klasse erstellt und vollständig gelöscht werden müssten. Aber das machen wir nicht. Warum fragst du? Was passiert, wenn eine der Methoden die Struktur der Tabelle ändert? Löschen Sie beispielsweise eine Spalte. In diesem Fall schlagen die verbleibenden Methoden möglicherweise fehl oder müssen wie erwartet reagieren (z. B. eine hintere Spalte erstellen). Wir müssen zugeben, dass wir dadurch eine unnötige Verbindung (Abhängigkeit) der Tests voneinander haben, die uns nichts nützt. Aber ich schweife ab, machen wir weiter...

Testen der findById-Methode

@Test
public void findByIdTest() {
   fillTables(new String[]{"fill_table_robots.sql"});

   Long id = getJdbcTemplate().queryForObject("SELECT id FROM robots WHERE name = 'Molly'", Long.class);
   Robot robot = countryDAO.findById(id);

   assertThat(robot).isNotNull();
   assertThat(robot.getId()).isEqualTo(id);
   assertThat(robot.getName()).isEqualTo("Molly");
   assertThat(robot.getCpu()).isEqualTo("AMD Ryzen 7 2700X");
   assertThat(robot.getProducer()).isEqualTo("China");
}
3 – Füllen Sie die Tabelle mit Testdaten. 5 – Erhalten Sie die ID für die benötigte Entität. 6 – Verwenden Sie die getestete Methode. 8...12 – Vergleichen Sie die empfangenen Daten mit den erwarteten

Methodentest aktualisieren

@Test
public void updateTest() {
   fillTables(new String[]{"fill_table_robots.sql"});

   Long robotId = getJdbcTemplate().queryForObject("SELECT id FROM robots WHERE name = 'Rex'", Long.class);

   Robot updateRobot = Robot.builder()
           .id(robotId)
           .name("Aslan")
           .cpu("Intel Core i5-3470")
           .producer("Narnia")
           .build();

   Robot responseRobot = countryDAO.update(updateRobot);
   Robot updatedRobot = getJdbcTemplate().queryForObject(
           "SELECT id, name, cpu, producer FROM robots WHERE id = ?",
           robotMapper(),
           robotId);

   assertThat(updatedRobot).isNotNull();
   assertThat(updateRobot.getName()).isEqualTo(responseRobot.getName());
   assertThat(updateRobot.getName()).isEqualTo(updatedRobot.getName());
   assertThat(updateRobot.getCpu()).isEqualTo(responseRobot.getCpu());
   assertThat(updateRobot.getCpu()).isEqualTo(updatedRobot.getCpu());
   assertThat(updateRobot.getProducer()).isEqualTo(responseRobot.getProducer());
   assertThat(updateRobot.getProducer()).isEqualTo(updatedRobot.getProducer());
   assertThat(responseRobot.getId()).isEqualTo(updatedRobot.getId());
   assertThat(updateRobot.getId()).isEqualTo(updatedRobot.getId());
}
3 – Füllen Sie die Tabelle mit Testdaten. 5 – Abrufen der ID der zu aktualisierenden Entität. 7 – Erstellen der aktualisierten Entität. 14 – Verwenden der getesteten Methode. 15 – Abrufen der aktualisierten Entität zur Überprüfung. 20...28 – Vergleichen der empfangenen Daten mit Das erwartete Testen der Update-Methode ähnelt dem Erstellen. Zumindest für mich. Sie können die Abstimmungen beliebig verdrehen: Es kann nie zu viele Schecks geben. Ich möchte außerdem darauf hinweisen, dass Tests keine Garantie für die volle Funktionalität oder Fehlerfreiheit geben. Tests stellen lediglich sicher, dass das tatsächliche Ergebnis des Programms (seines Fragments) dem erwarteten entspricht. In diesem Fall werden nur die Teile überprüft, für die Tests geschrieben wurden.

Lassen Sie uns einen Kurs mit Tests starten ...

Integrationstest einer Datenbank mit MariaDB als Ersatz für MySql - 7Sieg)) Lass uns Tee kochen und Kekse holen: Wir haben es verdient)) Integrationstest einer Datenbank mit MariaDB als Ersatz für MySql - 8

Nützliche Links

Für diejenigen, die mit dem Lesen fertig sind, vielen Dank für Ihre Aufmerksamkeit und... Integrationstest einer Datenbank mit MariaDB als Ersatz für MySql - 9

*epische Star-Wars-Musik*

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION