web-dev-qa-db-fra.com

Utilisation de Liquibase pour initialiser en mémoire H2 pour des tests d'unité dans l'application de démarrage à ressort

J'ai utilisé plusieurs fois des bases de données in-mem dans les tests JPA de printemps et je n'ai jamais eu de problème. Cette fois, j'ai un schéma un peu plus complexe pour initialiser, et ce schéma doit avoir un nom personnalisé (certaines des entités de notre modèle de domaine sont liées à un nom de catalogue spécifique.) Donc, pour cette raison, ainsi que pour assurer que les tests sont entièrement synchronisés et compatibles avec la façon dont nous initialisons et gèrent nos schémas, j'essaie d'initialiser une base de données H2 en mémoire à l'aide de Liquibase avant mon JPA Les tests d'unités de référentiel sont exécutés.

(Remarque: nous utilisons Boot de ressort 2.1.3.Release et MySQL Comme notre base de données principale, et H2 n'est utilisée que pour les tests .)

Je suis suivi du Guide de référence du ressort pour la mise en place Liquibase Executions sur le démarrage . J'ai les entrées suivantes dans mon Maven Pom :

    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.liquibase</groupId>
        <artifactId>liquibase-core</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-test-autoconfigure</artifactId>
        <scope>test</scope>
    </dependency>

Mes fichiers de test ressemblent à ceci:

 @RunWith(SpringRunner.class)
 @ContextConfiguration(classes = PersistenceTestConfig.class)
 @DataJpaTest
 public class MyRepositoryTest {

     @Autowired
     private MyRepository myRepository;

     @Test
     public void someDataAccessTest() {
         // myRepository method invocation and asserts here...
         // ...
     }
 }

La classe de contexte de l'application:

  @EnableJpaRepositories({"com.mycompany.myproject"})
  @EntityScan({"com.mycompany.myproject"})
  public class PersistenceTestConfig {

       public static void main(String... args) {
           SpringApplication.run(PersistenceTestConfig.class, args);
       }
  }

Selon le guide de référence,

Par défaut, Liquibase AutoWires la (@PRIMARY) DataSource dans votre contexte et utilise cela pour les migrations. Si vous devez utiliser une DataSource différente, vous pouvez en créer un et marquer son @bean comme @LiquibasedataSource. Si vous le faites et que vous voulez deux sources de données, n'oubliez pas de créer un autre et de le marquer comme @Primary. Vous pouvez également utiliser la source de données native de Liquibase en définissant Spring.Liquibase. [URL, utilisateur, mot de passe] dans des propriétés externes. Réglage de printemps.liquibase.url ou de printemps.liquibase.user est suffisant pour que Liquibase utilise sa propre source de données. Si l'une des trois propriétés n'a pas été définie, la valeur de sa propriété Spring.DataSource équivalente sera utilisée.

De toute évidence, je veux que mes tests utilisent la même instance de DataSource que celle utilisée par Liquibase pour initialiser la base de données. Donc, au début, j'ai essayé de spécifier le printemps.datasource Propriétés sans fournir le printemps.liquibase. [URL, utilisateur, mot de passe] Propriétés - En supposant que Liquibase serait alors Utilisez le type de données de printemps primaire par défaut:

spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS corp
spring.datasource.username=sa
spring.datasource.password=

spring.jpa.hibernate.ddl-auto=validate

# LIQUIBASE (LiquibaseProperties)
spring.liquibase.change-log=classpath:db.changelog.xml
#spring.liquibase.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS corp
#spring.liquibase.user=sa
#spring.liquibase.password=
spring.liquibase.default-schema=CORP
spring.liquibase.drop-first=true

Cela n'a pas fonctionné car Liquibase n'a pas trouvé le schéma CORP où i DOIT Dont mes tables créées:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in class path resource  [org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguratio n$LiquibaseConfiguration.class]: Invocation of init method failed; nested  exception is liquibase.exception.DatabaseException:  liquibase.command.CommandExecutionException: liquibase.exception.DatabaseException: liquibase.exception.LockException: liquibase.exception.DatabaseException: Schema "CORP" not found; SQL statement:
 CREATE TABLE CORP.DATABASECHANGELOGLOCK (ID INT NOT NULL, LOCKED BOOLEAN NOT NULL, LOCKGRANTED TIMESTAMP, LOCKEDBY VARCHAR(255), CONSTRAINT PK_DATABASECHANGELOGLOCK PRIMARY KEY (ID)) [90079-197] [Failed SQL: CREATE TABLE CORP.DATABASECHANGELOGLOCK (ID INT NOT NULL, LOCKED BOOLEAN NOT NULL, LOCKGRANTED TIMESTAMP, LOCKEDBY VARCHAR(255), CONSTRAINT PK_DATABASECHANGELOGLOCK PRIMARY KEY (ID))]

Donc, j'ai sorti les définitions de propriété Spring.DataSource explicite et fournissait uniquement les propriétés Liquibase suivantes:

 spring.jpa.hibernate.ddl-auto=validate

 # LIQUIBASE (LiquibaseProperties)
 spring.liquibase.change-log=classpath:db.changelog.xml
 spring.liquibase.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS corp
 spring.liquibase.user=sa
 spring.liquibase.password=
 spring.liquibase.default-schema=CORP
 spring.liquibase.drop-first=true

Cela a abouti à la tâche Liquibase exécutant avec succès et apparemment charger toutes les tables et données nécessaires à son natif DataSource au démarrage - à l'aide des fichiers Changelog fournis. Je comprends que cela se produit parce que j'ai explicitement définir les propriétés Liquibase DS) et, par documentation de printemps, cela entraînerait une utilisation de Liquibase à utiliser sa propre voie de données autochtone. Je suppose, pour cette raison, tandis que le Liquibase Trak fonctionne maintenant avec succès, les tests tentent toujours d'utiliser une [rayission par défaut?] DataSource, et le schéma de base de données échoue à la validation du pré-test. (Non Schéma de "Corp" trouvé, pas de tables.) Donc, il est donc évident que les tests utilisent une instance de DataSource différente de celle que j'essaye de générer avec Liquibase.

Comment faire les tests utilisez ce que Liquibase génère-t-il?

Rien que j'essaye semble travailler. Je soupçonne qu'il existe une sorte de conflit entre les configurations automatiques et explicites que j'utilise. Est @DataJpaTest une bonne approche dans ce cas. Je souhaite limiter la configuration de mon contexte d'application sur des tests strictement de la JPA, je n'ai besoin de rien d'autre pour ces tests.

Cela devrait être simple ... Cependant, je n'ai pas été en mesure de trouver la bonne voie, et je ne trouve aucune documentation qui expliquerait clairement comment résoudre ce problème.

Toute aide est très appréciée!

8
cvnew

Le problème réside dans @DataJpaTest vous utilisez. Voir la Documentation de @DataJpaTest

Par défaut, les tests annotés avec @DatajpaTest utiliseront une base de données intégrée dans la mémoire (remplacement de n'importe quelle source de données explicite ou généralement configurée automatiquement). L'annotation @AutoconfigureTestStAbase peut être utilisée pour remplacer ces paramètres.

Cela signifie que votre source de données configurée automatiquement est la nervation et l'URL spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS corp n'est pas pris en compte

Vous trouverez quelque chose de similaire dans le journal

EmbeddedDataSourceBeanFactoryPostProcessor : Replacing 'dataSource' DataSource bean with embedded version

Pour corriger, utiliser:

spring.test.database.replace=none
2
Lesiak