wake-up-neo.com

Wie erstelle ich eine Datenbank vor jedem Test im Frühjahr neu?

Meine Spring-Boot-Mvc-Web-Anwendung hat die folgende Datenbankkonfiguration in der Datei application.properties:

spring.datasource.url=jdbc:h2:tcp://localhost/~/pdk
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver

dies ist die einzige Konfiguration, die ich gemacht habe. Keine anderen von mir vorgenommenen Konfigurationen. Trotzdem werden die Spring- und Subsysteme bei jedem Webanwendungslauf automatisch neu erstellt. Die Datenbank wird nämlich beim Systemlauf neu erstellt, während sie nach dem Beenden der Anwendung Daten enthält.

Ich habe diese Standardeinstellungen nicht verstanden und erwartet, dass sie für Tests geeignet sind.

Als ich jedoch anfing, Tests durchzuführen, stellte ich fest, dass die Datenbank nur einmal neu erstellt wurde. Da die Tests in keiner vordefinierten Reihenfolge ausgeführt werden, ist dies überhaupt nicht sinnvoll.

Die Frage ist also: wie macht man einen Sinn? Das heißt. Wie erstelle ich die Datenbank vor jedem Test neu, wenn die Anwendung zum ersten Mal gestartet wird?

Mein Testklassenkopf ist wie folgt:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = myapp.class)
//@WebAppConfiguration
@WebIntegrationTest
@DirtiesContext
public class WebControllersTest {

Wie Sie sehen, habe ich @DirtiesContext Auf Klassenebene versucht und es hat nicht geholfen.

UPDATE

Ich habe eine Bohne

@Service
public class DatabaseService implements InitializingBean {

das hat eine Methode

@Override
    @Transactional()
    public void afterPropertiesSet() throws Exception {
        log.info("Bootstrapping data...");
        User user = createRootUser();
        if(populateDemo) {
            populateDemos();
        }
        log.info("...Bootstrapping completed");
    }

Jetzt habe ich die Methode populateDemos() erstellt, um alle Daten aus der Datenbank zu löschen. Leider wird es trotz @DirtiesContext Nicht vor jedem Test aufgerufen. Warum?

37
Dims

Eigentlich denke ich, dass Sie das wollen:

@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)

http://docs.spring.io/autorepo/docs/spring-framework/4.2.6.RELEASE/javadoc-api/org/springframework/test/annotation/DirtiesContext.html

@DirtiesContext kann als Annotation auf Klassen- und Methodenebene innerhalb derselben Klasse verwendet werden. In solchen Szenarien wird der ApplicationContext nach einer solchen mit Annotationen versehenen Methode sowie nach der gesamten Klasse als fehlerhaft markiert. Wenn der DirtiesContext.ClassMode auf AFTER_EACH_TEST_METHOD gesetzt ist, wird der Kontext nach jeder Testmethode in der Klasse als fehlerhaft markiert.

65
Raphael Amoedo

Um die Datenbank zu erstellen, müssen Sie das tun, was die anderen Antworten mit dem spring.jpa.hibernate.ddl-auto=create-drop, wenn Sie nun beabsichtigen, die Datenbank bei jedem Test zu erweitern, bietet spring eine sehr nützliche Anmerkung

@Transactional(value=JpaConfiguration.TRANSACTION_MANAGER_NAME)
@Sql(executionPhase=ExecutionPhase.BEFORE_TEST_METHOD,scripts="classpath:/test-sql/group2.sql")
public class GroupServiceTest extends TimeoffApplicationTests {

das ist aus diesem Paket org.springframework.test.context.jdbc.Sql; und Sie können eine Vorher-Test-Methode und eine Nachher-Test-Methode ausführen. Zum Auffüllen der Datenbank.

Wenn Sie die Datenbank jedes Mal erstellen möchten, nehmen wir an, Sie möchten nur, dass Ihr Test die Option create-drop hat. Mit dieser Anmerkung können Sie Ihre Tests mit benutzerdefinierten Eigenschaften konfigurieren

@TestPropertySource(locations="classpath:application-test.properties")
public class TimeoffApplicationTests extends AbstractTransactionalJUnit4SpringContextTests{

Ich hoffe es hilft

9
jstuartmilne

Mit Spring Boot kann die H2-Datenbank für jeden Test eindeutig definiert werden. Überschreiben Sie einfach die Datenquellen-URL für jeden Test

 @SpringBootTest(properties = {"spring.config.name=myapp-test-h2","myapp.trx.datasource.url=jdbc:h2:mem:trxServiceStatus"})

Die Tests können parallel laufen.

Innerhalb des Tests können die Daten von zurückgesetzt werden

@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
5
Interlated

Wenn du benutzt spring.jpa.hibernate.ddl-auto=create-drop sollte ausreichen, um eine Datenbank zu erstellen/zu löschen?

5
user2669657

Sofern Sie keine Spring-Data-Integration verwenden (die ich überhaupt nicht kenne), scheint dies eine benutzerdefinierte Logik zu sein, die Sie selbst implementieren müssen. Spring kennt Ihre Datenbanken, ihre Schemata und Tabellen nicht.

Angenommen, JUnit, schreiben Sie das entsprechende @Before und @After Methoden zum Einrichten und Bereinigen Ihrer Datenbank, ihrer Tabellen und Daten. Ihre Tests können die benötigten Daten selbst schreiben und gegebenenfalls nachträglich bereinigen.

Wenn Sie nach einer Alternative für den @DirtiesContext Suchen, hilft Ihnen der folgende Code. Ich habe Code von diese Antwort verwendet.

Richten Sie zuerst die H2-Datenbank in der Datei application.yml In Ihrem Testressourcenordner ein:

spring: 
  datasource:
    platform: h2
    url: jdbc:h2:mem:test
    driver-class-name: org.h2.Driver
    username: sa
    password:

Danach erstellen Sie eine Klasse mit dem Namen ResetDatabaseTestExecutionListener:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;

import javax.sql.DataSource;
import Java.sql.Connection;
import Java.sql.ResultSet;
import Java.sql.SQLException;
import Java.sql.Statement;
import Java.util.HashSet;
import Java.util.Set;

public class ResetDatabaseTestExecutionListener extends AbstractTestExecutionListener {

    @Autowired
    private DataSource dataSource;

    public final int getOrder() {
        return 2001;
    }

    private boolean alreadyCleared = false;

    @Override
    public void beforeTestClass(TestContext testContext) {
        testContext.getApplicationContext()
                .getAutowireCapableBeanFactory()
                .autowireBean(this);
    }

    @Override
    public void prepareTestInstance(TestContext testContext) throws Exception {

        if (!alreadyCleared) {
            cleanupDatabase();
            alreadyCleared = true;
        }
    }

    @Override
    public void afterTestClass(TestContext testContext) throws Exception {
        cleanupDatabase();
    }

    private void cleanupDatabase() throws SQLException {
        Connection c = dataSource.getConnection();
        Statement s = c.createStatement();

        // Disable FK
        s.execute("SET REFERENTIAL_INTEGRITY FALSE");

        // Find all tables and truncate them
        Set<String> tables = new HashSet<>();
        ResultSet rs = s.executeQuery("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES  where TABLE_SCHEMA='PUBLIC'");
        while (rs.next()) {
            tables.add(rs.getString(1));
        }
        rs.close();
        for (String table : tables) {
            s.executeUpdate("TRUNCATE TABLE " + table);
        }

        // Idem for sequences
        Set<String> sequences = new HashSet<>();
        rs = s.executeQuery("SELECT SEQUENCE_NAME FROM INFORMATION_SCHEMA.SEQUENCES WHERE SEQUENCE_SCHEMA='PUBLIC'");
        while (rs.next()) {
            sequences.add(rs.getString(1));
        }
        rs.close();
        for (String seq : sequences) {
            s.executeUpdate("ALTER SEQUENCE " + seq + " RESTART WITH 1");
        }

        // Enable FK
        s.execute("SET REFERENTIAL_INTEGRITY TRUE");
        s.close();
        c.close();
    }
}

Der obige Code setzt die Datenbank zurück (Tabellen abschneiden, Sequenzen zurücksetzen usw.) und ist für die Arbeit mit der H2-Datenbank vorbereitet. Wenn Sie eine andere Speicherdatenbank (wie HsqlDB) verwenden, müssen Sie die erforderlichen Änderungen an den SQLs-Abfragen vornehmen, um dasselbe zu erreichen.

Gehen Sie danach zu Ihrer Testklasse und fügen Sie die Annotation @TestExecutionListeners Wie folgt hinzu:

@TestExecutionListeners(mergeMode =
        TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS,
        listeners = {ResetDatabaseTestExecutionListener.class}
)
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CreateOrderIT {

Das sollte funktionieren.

Wenn Sie zwischen diesem Ansatz und @DirtiesContext Keinen Leistungsunterschied feststellen, wahrscheinlich verwenden Sie @MockBean In Ihren Tests Frühling Kontext.

2
Dherik