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 sur default 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?

23
demandé sur kolen 2013-02-07 15:46:43

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:

8
répondu mguillermin 2013-02-24 20:46:23

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.

5
répondu kolen 2013-02-28 13:50:16

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?

1
répondu EECOLOR 2017-05-23 12:24:58

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
        }
    });
}
1
répondu Piotr Romanowski 2014-03-27 16:18:09

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 ).

1
répondu cchantep 2014-08-08 11:15:27