2010-04-21 16 views
9

J'utilise HSQLDB pour les tests d'intégration de couche de données, ce qui est génial. Cependant, je constate que mes contraintes de clé étrangère entravent mes tests. Par exemple, pour tester une sélection simple sur une table, je dois insérer des données factices dans cinq tables supplémentaires. Cela me donne envie de jeter des choses.TDD avec HSQLDB - suppression des clés étrangères

J'ai des annotations JPA dans notre code de modèle, et j'ai configuré Hibernate pour recréer le schéma (hbm2ddl.create-drop) dans la configuration. Les jointures sont interprétées correctement en tant que contraintes de clé étrangère lorsque les tables sont générées.

Ce que je voudrais est soit:

  1. pas créer les clés étrangères d'abord (idéal, plus propre), ou
  2. Trouver un moyen de laisser tomber toutes les clés programme étrangères dans la base de données (un peu hacky mais fera le travail)

Si c'est utile, j'utilise Spring pour autowire ces tests. Les tests en question héritent de AbstractTransactionalJUnit4SpringContextTests.

Qu'en pensez-vous? Cela peut-il être fait?

Répondre

8

Vous pouvez désactiver les contraintes FK à l'instruction suivante:

SET REFERENTIAL_INTEGRITY FALSE; 

Vous pouvez l'exécuter via un JDBC Statement avant vos méthodes d'essai (et le ramener à TRUE après).

+1

Merci! Vous venez de couper à moi seul ma classe de test en deux. Il est intéressant de noter que cela facilite également l'extraction de la racine où Hibernate provoque des jointures internes inutiles. – roufamatic

+0

Pour quiconque utilise AbstractTransactionalJUnit4SpringContextTests ... la magie est la suivante: 'simpleJdbcTemplate.getJdbcOperations(). Execute (" SET REFERENTIAL_INTEGRITY FALSE; ");' – roufamatic

+0

@roufamatic Content de vous trouver utile. –

0

Je considérerais passer un certain temps sur la création de quelques appareils, éventuellement avec DBUnit, que vous insérez @Before.

BTW, AbstractTransactionalJUnit4Test est dépréciée au printemps 3.0

+0

re: ... je voulais dire dépréciée « AbstractTransactionalJUnit4SpringContextTests » que je CORRIGÉS ci-dessus. FWIW nous utilisons Spring 2.5. – roufamatic

8

J'ai rencontré exactement le même problème en essayant de tester mon DAO avec un jeu de données XML. Config est DbUnit + HSQLDB 2.2.8 + junit4 + printemps + JPA-> tous ensemble menant à

java.sql.SQLIntegrityConstraintViolationException: integrity constraint violation: foreign key no parent; FK13EE6CE6F09A6AAC table: **** 

J'ai trouvé un palliatif en mettant en place un écouteur AbstractTestExecutionListener extension. Vous devez spécifier le type d'action à effectuer avant chaque test, dans notre cas, en désactivant les contraintes de clé étrangère. REMARQUE: la syntaxe peut varier en fonction de la version de HSQLDB utilisée.

public class ForeignKeyDisabling extends AbstractTestExecutionListener {  
    @Override 
    public void beforeTestClass(TestContext testContext) throws Exception { 
     IDatabaseConnection dbConn = new DatabaseDataSourceConnection(
       testContext.getApplicationContext().getBean(DataSource.class) 
       ); 
     dbConn.getConnection().prepareStatement("SET DATABASE REFERENTIAL INTEGRITY FALSE").execute(); 

    } 
} 

Il vous suffit alors d'ajouter cet écouteur dans la collection déjà en place dans vos tests:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration({"applicationContext-test.xml"}) 
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DataSetTestExecutionListener.class, ForeignKeyDisabling.class}) 
+0

C'est une façon élégante de le faire. – roufamatic

+1

ne devriez-vous pas le remettre au début de chaque test (après la configuration de la base de données) avec "SET DATABASE REFERENTIAL INTEGRITY TRUE"? – wannabeartist

+0

La définition d'un tel code dans setUp peut également répondre à vos besoins. Dans mon cas, toute la suite de tests dépendait de cette modification de structure. Mais le résultat devrait être le même. –

1

base sur l'inspiration dans ce fil, j'ai créé une solution un peu plus robuste pour ce problème. Le point était, j'aime vraiment les contraintes lors de l'exécution du test et toutes les autres solutions l'ont gardé désactivé. Ce code les désactivera uniquement pendant la durée de l'importation de l'ensemble de données, puis les réactivera. Et peut être facilement étendu pour supporter un autre moteur de DB:

import com.github.springtestdbunit.DbUnitTestExecutionListener; 
import org.apache.log4j.Logger; 
import org.dbunit.database.DatabaseDataSourceConnection; 
import org.dbunit.database.IDatabaseConnection; 
import org.springframework.test.context.TestContext; 

import javax.sql.DataSource; 
import java.sql.Connection; 
import java.sql.SQLException; 

/** 
* Class DisableForeignKeysDbUnitTestExecutionListener 
* Simple wrapper class around DbUnitTestExecutionListener, which - for the time of importing the database - 
* disables Foreign Key Constraints checks. 
* This class can be extended by simply overriding toggleForeignKeysConstraintsForDbEngine(Connection, String, boolean); 
* subclasses should always call super-implementation for default case. 
*/ 
public class DisableForeignKeysDbUnitTestExecutionListener 
    extends DbUnitTestExecutionListener 
{ 
    private static final Logger logger = Logger.getLogger(DisableForeignKeysDbUnitTestExecutionListener.class); 
    private Connection cachedDbConnection; 

    @Override 
    public void beforeTestMethod(TestContext testContext) 
     throws Exception 
    { 
     this.toggleForeignKeysConstraints(testContext, false); 
     super.beforeTestMethod(testContext); 
     this.toggleForeignKeysConstraints(testContext, true); 
    } 

    /** 
    * Method should perform query to disable foreign keys constraints or return false, 
    * if it is not able to perform such query (e.g. unknown database engine) 
    * 
    * @param connection Database connection 
    * @param dbProductName Name of the database product (as reported by connection metadata) 
    * @param enabled  Expected state of foreign keys after the call 
    * 
    * @return True, if there was suitable statement for specified engine, otherwise false 
    * 
    * @throws SQLException 
    */ 
    protected boolean toggleForeignKeysConstraintsForDbEngine(Connection connection, String dbProductName, boolean enabled) 
     throws SQLException 
    { 
     switch (dbProductName) 
     { 
      case "HSQL Database Engine": 
       connection.prepareStatement("SET DATABASE REFERENTIAL INTEGRITY " + (enabled ? "TRUE" : "FALSE")) 
          .execute(); 
       return (true); 
     } 
     return (false); 
    } 

    private void toggleForeignKeysConstraints(TestContext testContext, boolean enabled) 
    { 
     try 
     { 
      Connection connection = this.getDatabaseConnection(testContext); 
      String databaseProductName = connection.getMetaData().getDatabaseProductName(); 
      if (!this.toggleForeignKeysConstraintsForDbEngine(connection, databaseProductName, enabled)) 
      { 
       throw new IllegalStateException("Unknown database engine '" + databaseProductName + 
                "'. Unable to toggle foreign keys constraints."); 
      } 
     } 
     catch (Throwable throwable) 
     { 
      logger.error("Unable to toggle Foreign keys constraints: " + throwable.getLocalizedMessage()); 
     } 
    } 

    synchronized private Connection getDatabaseConnection(TestContext testContext) 
     throws SQLException 
    { 
     if (this.cachedDbConnection == null) 
     { 
      DataSource dataSource = testContext.getApplicationContext().getBean(DataSource.class); 
      if (dataSource == null) 
      { 
       throw new IllegalStateException("Unable to obtain DataSource from ApplicationContext. " + 
                "Foreign constraints will not be disabled."); 
      } 

      IDatabaseConnection dsConnection = new DatabaseDataSourceConnection(dataSource); 
      this.cachedDbConnection = dsConnection.getConnection(); 
     } 

     return (this.cachedDbConnection); 
    } 
} 
Questions connexes