Comment créer des tests unitaires contre la base de données non en mémoire comme MySQL in Play framework, avec la réinitialisation à l'état connu?
je veux créer des tests unitaires qui couvrent le code qui utilisent la base de données relationnelle dans le cadre de jeu 2.1.0. Il y a beaucoup de possibilités pour cela et tous causent des problèmes:
Essai sur la mémoire de la base de données H2
Play framework documentation propose d'exécuter des tests unitaires sur la base de données en mémoire H2, même si la base de données principale utilisée pour le développement et la production utilise d'autres logiciels (i.e. MySQL):
app = Helpers.fakeApplication(Helpers.inMemoryDatabase());
ma demande n'utilisez pas de fonctionnalités RDBMS compliquées telles que les procédures stockées et la plupart des cas d'accès à la base de données sont des appels ebean, il devrait donc être compatible avec MySQL et H2.
cependant, les instructions de création de table dans evolutions utilisent des fonctionnalités spécifiques à MySQL, telles que la spécification ENGINE = InnoDB
, DEFAULT CHARACTER SET = utf8
, etc. Je crains que si je supprime ces parties propriétaires de CREATE TABLE
, MySQL va utiliser certains paramètres par défaut que je ne peux pas contrôler et qui dépendent de la version, donc pour tester et développer la configuration MySQL principale de l'application doit être modifiée.
Quelqu'un a utilisé cette approche (rendre les évolutions compatibles avec MySQL et H2)?
autres idées comment il peut être manipulé:
- Evolutions séparées pour MySQL et H2 (pas une bonne idée)
- une façon de faire ignorer par H2 des trucs MySQL supplémentaires dans
create table
(le mode de compatibilité MySQL ne fonctionne pas, il se plaint toujours même surdefault character set
). Je ne sais pas comment.
test sur le même pilote de base de données que la base de données principale
le seul avantage de H2 base de données en mémoire qu'il est rapide, et les essais sur le même pilote de base de données que dev/base de données de production peut être mieux, parce qu'il est plus proche de l'environnement réel.
comment cela peut-il être fait correctement dans le cadre de jeu?
essayé:
Map<String, String> settings = new HashMap<String, String>();
settings.put("db.default.url", "jdbc:mysql://localhost/sometestdatabase");
settings.put("db.default.jndiName", "DefaultDS");
app = Helpers.fakeApplication(settings);
ressemble à des évolutions travailler ici, mais comment est-il préférable de nettoyer la base de données avant chaque test? En créant du code personnalisé qui tronque chaque table? Si les tables sont désactivées, les évolutions seront-elles à nouveau exécutées avant le prochain test, ou seront-elles appliquées une fois par commande play test
? Ou une fois par invocation Helpers.fakeApplication()
?
Quelles sont les meilleures pratiques? Entendu parler de dbunit , est-il possible de l'intégrer sans beaucoup de douleur et de bizarreries?
5 réponses
tout d'abord, je vous recommande d'utiliser les mêmes RDBMS pour les tests et la production car cela pourrait éviter certains bugs difficiles à trouver.
concernant la nécessité de nettoyer votre base de données entre chaque test, vous pouvez utiliser Ebean DdlGenerator
pour générer des scripts pour créer une base de données propre et l'annotation @Before
de JUnit pour exécuter automatiquement ces scripts avant chaque test.
en utilisant le DdlGenerator
peut être fait comme ceci:
EbeanServer server = Ebean.getServer(serverName);
ServerConfig config = new ServerConfig();
DdlGenerator ddl = new DdlGenerator((SpiEbeanServer) server, new MySqlPlatform(), config);
ce code peut être placé dans une classe de base que vous pourriez faire hériter vos tests (ou à l'intérieur d'une coutume Runner
que vous pouvez utiliser avec l'annotation @RunWith
).
il vous permettra également d'automatiser facilement la création FakeApplication
, en évitant un code boilerplate.
quelques liens qui peuvent être utiles:
j'ai utilisé le même moteur de base de données que la base de données principale et dbunit pour nettoyer avant chaque essai.
public class SomeTest {
// ...
@Before
public void startApp() throws Exception {
// Set up connection to test database, different from main database. Config better should be used instead of hard-coding.
Map<String, String> settings = new HashMap<String, String>();
settings.put("db.default.url", "jdbc:mysql://localhost/somedatabase?characterEncoding=UTF-8&useOldAliasMetadataBehavior=true");
settings.put("db.default.user", "root");
settings.put("db.default.password", "root");
settings.put("db.default.jndiName", "DefaultDS"); // make connection available to dbunit through JNDI
app = Helpers.fakeApplication(settings);
Helpers.start(app);
databaseTester = new JndiDatabaseTester("DefaultDS");
IDataSet initialDataSet = new FlatXmlDataSetBuilder().build(play.Play.application()
.resourceAsStream("/resources/dataset.xml"));
databaseTester.setDataSet(initialDataSet);
databaseTester.onSetup();
}
@After
public void stopApp() throws Exception {
databaseTester.onTearDown();
Helpers.stop(app);
}
}
mon dataset.xml
contient juste des noms de table pour dire à dbunit de vider ces tables avant chaque test. Il peut également contenir des accessoires.
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<name_of_my_first_table />
<name_of_my_second_table />
</dataset>
les évolutions s'exécutent automatiquement sur la base de données de test en utilisant cette approche, donc si vous supprimez toutes les tables de la base de données de test, elles seront recréées.
il est exagéré d'utiliser dbunit si vous avez seulement besoin de nettoyer les tables, vous pouvez les nettoyer en émettant la requête directement ou en utilisant ebean DdlGenerator
. Mais j'utilise aussi dbunit pour comparer les données.
Je n'utilise pas Helpers.running
, car il prend Runnable
et Runnable
implémentations ne peuvent pas jeter exceptions - très peu pratique pour les tests. Mais si vous regardez le code pour running()
, il appelle juste Helpers.start()
et Helpers.stop()
donc je les appelle méthodes directement dans @Before
et @After
.
a décidé de ne pas utiliser H2 pour exécuter des tests: oui, il court plus vite, mais il y a trop de différence entre lui et MySQL.
Quelqu'un a utilisé cette approche (rendre les évolutions compatibles avec MySQL et H2)?
j'ai trouvé une réponse pour les caractéristiques spécifiques de MySQL: Comment puis-je tester l'unité pour la base de données MySQL avec Play 2.x?
quand j'ai écrit mes tests pour ma base de données postgres j'ai simplement créé un HashMap pour me connecter à la base de données, puis j'ai écrit des requêtes de test pour m'assurer que le nombre correct d'enregistrements existe et ainsi de suite... Voici mon code.
@Test
public void testDataBase() {
final HashMap<String,String> postgres = new HashMap<String, String>();
postgres.put("db.default.driver","org.postgresql.Driver");
postgres.put("db.default.url","jdbc:postgresql://localhost/myDataBase");
postgres.put("db.default.user", "postgres");
postgres.put("db.default.password", "password");
running(fakeApplication(postgres), new Runnable() {
@Override
public void run() {
//Insert Assertions Here
}
});
}
vous pouvez également utiliser DB mock, si le but est de valider à la fois votre Slick|JPA|Anorm mappings & fonctions basées sur.
quand il est adapté, il a l'avantage d'être plus conforme aux tests unitaires qu'un test DB, et plus facile à gérer (Pas de configuration/tâches claires, pas de synchronisation des tests pour éviter l'accès aux mêmes tables de test).
vous pouvez jeter un oeil à Acolyte (acolyte.eu.org ) qui est utilisé dans les spécifications de L'Anorme elle-même (par exemple https://github.com/playframework/playframework/blob/master/framework/src/anorm/src/test/scala/anorm/SqlResultSpec.scala ).