32 votes

Test unitaire des bases de données

L'été dernier, j'ai développé une application CRUD ASP.NET/SQL Server de base, et les tests unitaires étaient l'une des exigences. J'ai rencontré quelques problèmes lorsque j'ai essayé de tester contre la base de données. D'après ce que j'ai compris, les tests unitaires doivent être :

  • apatride
  • indépendants les uns des autres
  • répétable avec les mêmes résultats, c'est-à-dire sans changement persistant.

Ces exigences semblent être contradictoires lors du développement pour une base de données. Par exemple, je ne peux pas tester Insert() sans m'assurer que les lignes à insérer ne sont pas encore là, et je dois donc appeler Delete() en premier. Mais, que se passe-t-il s'ils ne sont pas déjà là ? Dans ce cas, je dois d'abord appeler la fonction Exists().

Ma solution finale impliquait de très grandes fonctions de configuration (beurk !) et un scénario de test vide qui s'exécuterait en premier et indiquerait que la configuration s'est déroulée sans problème. C'est un sacrifice sur l'indépendance des tests tout en maintenant leur absence d'état.

Une autre solution que j'ai trouvée consiste à intégrer les appels de fonctions dans une transaction qui peut être facilement annulée, comme suit XtUnit de Roy Osherove . Cela fonctionne, mais cela implique une autre bibliothèque, une autre dépendance, et cela semble une solution un peu trop lourde pour le problème en question.

Alors, qu'a fait la communauté des OS lorsqu'elle a été confrontée à cette situation ?


a dit tgmdbm :

Vous utilisez généralement votre cadre de test unitaire automatisé préféré pour effectuer des tests d'intégration, ce qui est pourquoi certaines personnes sont confuses, mais ils ne suivent pas les mêmes règles. Vous êtes autorisé à impliquer l'implémentation concrète l'implémentation concrète de plusieurs de vos classes (parce qu'elles ont été testées unitairement). Vous testez comment votre béton classes concrètes interagissent entre elles et avec la base de données .

Donc si je lis correctement, il n'y a pas vraiment de moyen de efficacement Test unitaire d'une couche d'accès aux données. Ou bien, un "test unitaire" d'une couche d'accès aux données consisterait-il à tester, par exemple, le SQL/les commandes générées par les classes, indépendamment de l'interaction réelle avec la base de données ?

2voto

Toran Billups Points 10012

J'ai expliqué une technique que j'ai utilisée pour cette situation. ici .

L'idée de base est d'exercer chaque méthode de votre DAL - d'affirmer vos résultats - et lorsque chaque test est terminé, de revenir en arrière afin que votre base de données soit propre (pas de données inutiles/de test).

Le seul problème que vous ne trouverez peut-être pas "génial" est que je fais généralement un test CRUD complet (pas pur du point de vue des tests unitaires) mais ce test d'intégration vous permet de voir votre code CRUD + mapping en action. Ainsi, s'il se casse, vous le saurez avant de lancer l'application (ce qui m'évite une tonne de travail lorsque j'essaie d'aller vite).

1voto

Eric Z Beard Points 18473

Ce que vous devriez faire, c'est exécuter vos tests à partir d'une copie vierge de la base de données que vous générez à partir d'un script. Vous pouvez exécuter vos tests et ensuite analyser les données pour vous assurer qu'elles ont exactement ce qu'elles devraient avoir après l'exécution de vos tests. Ensuite, vous supprimez simplement la base de données, puisque c'est un jetable. Tout ceci peut être automatisé, et peut être considéré comme une action atomique.

1voto

Mustafa Ekici Points 2879

Tester la couche de données et la base de données ensemble laisse peu de surprises pour plus tard dans la projet. Mais tester contre la base de données a ses problèmes, le principal étant que vous testez contre un état partagé par de nombreux tests. Si vous insérez une ligne dans la base de données dans un test, le test suivant peut également voir cette ligne.
Ce dont vous avez besoin, c'est d'un moyen de revenir en arrière sur les modifications apportées à la base de données.
Le site TransactionScope est suffisamment intelligente pour gérer des transactions très compliquées, ainsi que les transactions imbriquées où votre code à tester appelle les commits sur sa propre transaction locale. Voici un morceau de code simple qui montre comment il est facile d'ajouter la capacité de retour en arrière à vos tests :

    [TestFixture]
    public class TrannsactionScopeTests
    {
        private TransactionScope trans = null;

        [SetUp]
        public void SetUp()
        {
            trans = new TransactionScope(TransactionScopeOption.Required);
        }

        [TearDown]
        public void TearDown()
        {
            trans.Dispose();
        }

        [Test]
        public void TestServicedSameTransaction()
        {
            MySimpleClass c = new MySimpleClass();
            long id = c.InsertCategoryStandard("whatever");
            long id2 = c.InsertCategoryStandard("whatever");
            Console.WriteLine("Got id of " + id);
            Console.WriteLine("Got id of " + id2);
            Assert.AreNotEqual(id, id2);
        }
    }

0voto

Aaron Powell Points 15598

Si vous utilisez LINQ to SQL comme ORM, vous pouvez générer la base de données à la volée (à condition d'avoir un accès suffisant depuis le compte utilisé pour les tests unitaires). Voir http://www.aaron-powell.com/blog.aspx?id=1125

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X