web-dev-qa-db-fra.com

Comment tester la couche d'accès aux données?

J'ai une méthode DAO qui utilise Spring pour l'accès JDBC. Il calcule le taux de réussite d'un vendeur à vendre un article.

Voici le code:

public BigDecimal getSellingSuccessRate(long seller_id) {
    String sql = "SELECT SUM(IF(sold_price IS NOT NULL, 1, 0))/SUM(1) 
                  FROM transaction WHERE seller_id = ?";
    Object[] args = {seller_id};
    return getJdbcTemplate().queryForObject(sql, args, BigDecimal.class);
}

Comment dois-je procéder pour tester cette méthode ou toute autre méthode DAO avec JUnit? Quelles sont les meilleures pratiques pour tester la logique d'accès aux données? Je pense à le tester par rapport à une base de données embarquable chargée de certaines données, mais ne devrions-nous pas faire des tests d'intégration similaires à un environnement de production en termes de SGBDR et de schéma?

17
Michael

Le problème avec l'utilisation d'une "vraie" base de données pour les tests unitaires est la configuration, la suppression et l'isolement des tests. Vous ne voulez pas avoir à créer une base de données MySQL entièrement nouvelle et à créer des tables et des données juste pour un test unitaire. Les problèmes avec cela ont à voir avec la nature externe de la base de données et votre base de données de test est en panne, vos tests unitaires échouent. Il y a également des problèmes à vérifier que vous disposez d'une base de données unique pour les tests. Ils peuvent être surmontés, mais il existe une réponse plus simple.

Se moquer de la base de données est une option cependant il ne teste pas les requêtes réelles qui sont exécutées. Il peut être utilisé comme une solution beaucoup plus simple lorsque vous voulez vous assurer que les données du DAO passent correctement par le système. Mais pour tester le DAO lui-même, vous avez besoin de quelque chose derrière le DAO pour que les données et les requêtes s'exécutent correctement.

La première chose à faire est d'utiliser une base de données en mémoire. HyperSQL est un excellent choix pour cela car il a la capacité d'émuler le dialecte d'une autre base de données - de sorte que les différences mineures entre les bases de données restent les mêmes (types de données, fonctions, etc.). hsqldb a également quelques fonctionnalités intéressantes pour les tests unitaires.

db.url=jdbc:hsqldb:file:src/test/resources/testData;shutdown=true;

Cela charge l'état de la base de données (les tables, les données initiales) à partir du fichier testData. shutdown=true fermera automatiquement la base de données à la fermeture de la dernière connexion.

En utilisant injection de dépendance , demandez aux tests unitaires de sélectionner une base de données différente que celle utilisée par les builds de production (ou test, ou local) .

Votre DAO utilise ensuite la base de données injectée pour laquelle vous pouvez lancer des tests sur la base de données.

Les tests unitaires ressembleront alors à quelque chose (tas de trucs ennuyeux non inclus pour plus de brièveté):

    @Before
    public void setUpDB() {
        DBConnection connection = new DBConnection();
        try {
            conn = connection.getDBConnection();
            insert = conn.prepareStatement("INSERT INTO data (txt, ts, active) VALUES (?, ?, ?)");
        } catch (SQLException e) {
            e.printStackTrace();
            fail("Error instantiating database table: " + e.getMessage());
        }
    }

    @After
    public void tearDown() {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    private void addData(String txt, Timestamp ts, boolean active) throws Exception {
        insert.setString(1, txt);
        insert.setTimestamp(2, ts);
        insert.setBoolean(3, active);
        insert.execute();
    }

    @Test
    public void testGetData() throws Exception {
        // load data
        Calendar time = Calendar.getInstance();
        long now = time.getTimeInMillis();
        long then1h = now - (60 * 60 * 1000);  // one hour ago
        long then2m = now - (60 * 1000 * 2);   // two minutes ago
        addData("active_foo", new Timestamp(then1h), true);     // active but old
        addData("inactive_bar", new Timestamp(then1h), false);  // inactive and old
        addData("active_quz", new Timestamp(then2m), true);     // active and new
        addData("inactive_baz", new Timestamp(then2m), false);  // inactive and new

        DataAccess dao = new DataAccess();
        int count = 0;
        for (Data data : dao.getData()) {
            count++;
            assertTrue(data.getTxt().startsWith("active"));
        }

        assertEquals("got back " + count + " rows instead of 1", count, 1);
    }

Et donc, vous avez un test unitaire qui appelle le DAO et utilise les données qui ont été configurées dans une base de données à la volée qui existe pour la durée du test. Vous n'avez pas à vous soucier des ressources externes ou de l'état de la base de données avant l'exécution, ou de la restauration à un état connu (enfin, "l'état connu" est "n'existe pas", ce qui est trivial pour revenir à).

DBUnit peut faire beaucoup de ce que j'ai décrit un processus plus simple dans la configuration de la base de données, la création des tables et le chargement des données. Si vous deviez utiliser la base de données réelle pour une raison quelconque, c'est de loin le meilleur outil à utiliser.

Le code ci-dessus fait partie d'un projet maven que j'ai écrit pour la preuve de concept TestingWithHsqldb sur github

15
user40980

Tout d'abord, vous ne devez jamais effectuer de tests dans un environnement de production. Vous devez disposer d'un environnement de test qui reflète votre environnement de production et y effectuer des tests d'intégration.

Si vous faites cela, vous pouvez faire un certain nombre de choses.

  • Écrivez des tests unitaires qui testent pour voir si le SQL approprié est soumis à un élément factice à l'aide d'un cadre de simulation tel que Mockito. Cela garantira que votre méthode fait ce qu'elle est censée faire et supprime l'intégration de l'image.
  • Écrivez des scripts SQL de test démontrant la pertinence du SQL que vous avez testé dans vos tests unitaires. Cela peut aider à résoudre tout problème de réglage que vous pourriez rencontrer, car vous pouvez également exécuter des explications et autres en fonction de vos scripts de test.
  • Utilisez DBUnit, comme mentionné par @Sergio.
2
Matthew Flynn

Sur notre projet, chaque développeur exécute une base de données vide, sa structure est la même que la base de données de production.

Dans chaque test unitaire TestInitialize, nous créons une connexion et une transaction à la base de données ainsi que certains objets par défaut dont nous avons besoin pour chaque test. Et tout est annulé après la fin de chaque méthode ou classe.

De cette façon, il est possible de tester la couche sql. En fait, chaque requête ou appel de base de données doit être testé de cette manière.

L'inconvénient est qu'il est lent, nous l'avons donc mis dans un projet distinct de nos tests unitaires réguliers. Il est possible d'accélérer cela en utilisant une base de données en mémoire, mais l'idée reste la même.

1
Carra