web-dev-qa-db-fra.com

Stubbing / mocking a database in .Net

J'ai un webservice qui exécute simplement quelques procédures stockées, transforme les données et les envoie au navigateur. Pas de mappeur ORM sophistiqué ou quelque chose comme ça. Pour pouvoir écrire un test sans accéder à la base de données, j'ai fait ce qui suit:

  • J'ai extrait tous les appels à la base de données dans une seule classe. Les méthodes renvoient uniquement les objets DataSet et DataTable.
  • Exécuté un exemple d'appel pour chaque méthode et sérialisé le DataSet/DataTable sur le disque.
  • Extrait d'une interface exposant toutes les méthodes disponibles.
  • Implémentation d'une fausse classe de base de données qui charge simplement les données sérialisées et les renvoie.

Maintenant, j'ai des exemples de résultats sérialisés que je peux vérifier avec mon projet et je peux utiliser la fausse base de données dans mes tests.

Cela fonctionne assez bien pour moi. Existe-t-il un cadre qui facilite la création et le chargement des exemples de données? Mon projet actuel est petit, mais j'utiliserais le même schéma dans des projets plus importants.

Mise à jour:

De toute évidence, toutes les réponses ne sont pas fausses, mais rater le point. Je connais les bases des tests unitaires. Mais mon code fonctionne avec DataTables, donc je devrais en quelque sorte truquer mes DataTables. Construire un DataTable à partir de zéro n'est pas une tâche facile, et cela alourdirait mes tests et réduirait la lisibilité. Dans mon cas, il serait tout à fait impossible de générer manuellement des données d'échantillonnage utiles.

Par conséquent, j'ai exécuté quelques exemples d'appels sur une base de données exemple pour obtenir des DataTables. J'ai sérialisé ces tables sur le disque et j'utilise les versions sérialisées pour créer mes faux DataTables lors des tests. De cette façon, les tests sont indépendants de la base de données.

Il existe différentes options pour structurer le code afin de faciliter la désérialisation des tables. Mais ce sont des détails d'implémentation qui ne nécessitent pas de discussion à ce stade. Mon problème est le suivant:

La gestion des exemples d'appels et la (dé) sérialisation des tables est un travail fastidieux. Je cherchais des outils pour faciliter cela.

33
Achim

En lisant les autres réponses et les divers commentaires que vous avez faits, il semble que vous souhaitiez un moyen plus simple de générer de grands ensembles de données peuplés pour des tests d'intégration qui n'atteignent pas la base de données.

NBuilder est une excellente bibliothèque open source que j'ai réussi à créer de grandes quantités de données de test. Combinez simplement NBuilder, quelques classes d'objets POCO de base et quelques réflexions - vous aurez beaucoup de tables de données énormes que vous pouvez facilement combiner en ensembles de données en un rien de temps:

public class Person
{
    public string First { get; set; }
    public string Last { get; set; }
    public DateTime Birthday { get; set; }
}

private DataTable GenerateDataTable<T>(int rows)
{
    var datatable = new DataTable(typeof(T).Name);
    typeof(T).GetProperties().ToList().ForEach(
        x => datatable.Columns.Add(x.Name));
    Builder<T>.CreateListOfSize(rows).Build()
        .ToList().ForEach(
            x => datatable.LoadDataRow(x.GetType().GetProperties().Select(
                y => y.GetValue(x, null)).ToArray(), true));
    return datatable;
}

var dataset = new DataSet();
dataset.Tables.AddRange(new[]{
        GenerateDataTable<Person>(50),
        GenerateDataTable<Dog>(100)});
30
Bermo

Pour tester la transformation unitaire, vous ne devriez vraiment pas avoir besoin de se moquer de la base de données. Je soupçonne que vous avez étroitement couplé les transformations avec vos appels de base de données. Ce que vous voulez faire ici, c'est extraire toute votre logique de transformation dans une classe à part comme celle-ci:

public static Transformations
{
    public static DataSet TransformationA(DataSet dataSet)
    {
        //transformation logic here
    }

    public static DataSet TransformationB(DataSet dataSet)
    {
        //transformation logic here
    }
}

Avec cela, vous pouvez tester uniquement la logique des transformations en passant un ensemble de données, puis en affirmant que l'ensemble de données renvoyé a les transformations correctes qui lui sont appliquées. Cela vous évitera d'avoir à implémenter un autre magasin de données (votre "fausse" base de données) à des fins de test uniquement.

J'espère que cela aide

6
stuartf

Vous pouvez simuler votre classe DataAccess avec Rhinomocks et renvoyer une fausse table de données. Vous pouvez donc tester le code qui utilise ce DataTable.

var mockedDatatable= GetMockdt();

var mocks = new MockRepository();
var dal = mocks.StrictMock<DataAccess>();

using (mocks.Record())
{
  Expect.Call(dal.GetDataTableFromDatabase("", null)).Return(mockedDatatable).IgnoreArguments();
}

using (mocks.Playback())
{
  new SomeClass(dal);
}

METTRE À JOUR le message mockdt

private static DataTable GetMockdt()
{
  var dt = new DataTable();

  dt.Columns.Add("pageHeader");
  dt.Columns.Add("templatename");
  dt.Columns.Add("pageText");
  dt.Columns.Add("pageTitleBar");
  dt.Columns.Add("metaDescription");
  dt.Columns.Add("pageStartCode");
  dt.Columns.Add("pageEndCode");
  dt.Columns.Add("templateStartCode");
  dt.Columns.Add("templateEndCode");
  dt.Columns.Add("Author");
  dt.Columns.Add("version_date");
  dt.Columns.Add("pageurl");
  dt.Columns.Add("type");
  dt.Columns.Add("isparent");
  dt.Columns.Add("pagename");
  dt.Columns.Add("parentname");
  dt.Columns.Add("url");

  var mockRow = dt.NewRow();

  mockRow["pageHeader"] = "homepage";
  mockRow["pageText"] = "<p>home</p>";
  mockRow["templatename"] = "home";
  mockRow["pageTitleBar"] = "homepages";
  mockRow["metaDescription"] = "homepages";
  mockRow["pageStartCode"] = "homepages";
  mockRow["pageEndCode"] = "homepages";
  mockRow["templateStartCode"] = "homepages";
  mockRow["templateEndCode"] = "homepages";
  mockRow["Author"] = "someone";
  mockRow["version_date"] = "";
  mockRow["pageurl"] = "home";
  mockRow["type"] = "internal";
  mockRow["isparent"] = "true";
  mockRow["pagename"] = "homepage";
  mockRow["parentname"] = "root";
  mockRow["url"] = "homepage";

  dt.Rows.Add(mockRow);

  return dt;
}
2
Ivo

Découvrez https://github.com/nbuilder/nbuilder

"Qu'Est-ce que c'est?

Grâce à une interface fluide et extensible, NBuilder vous permet de créer rapidement des données de test, en attribuant automatiquement des valeurs aux propriétés et aux champs publics qui sont du type des types de données .NET intégrés (par exemple, les entrées et les chaînes). NBuilder vous permet de remplacer les propriétés qui vous intéressent en utilisant des expressions lambda. "

0
Ivo

D'après mon expérience, il a été assez facile de faire fonctionner les tests de bout en bout avec Fluent NHibernate. Il n'y a aucune excuse pour ne pas utiliser une couche aussi légère quand elle fait tant pour vous.

Test de spécification de persistance

0
GregC

Il n'y a pas d'outils pour faire ce que vous voulez en raison de vos exigences que vos données soient stockées en tant que DataTables, et vous avez besoin de données originales de la base de données. La partie manuelle des outils consiste à câbler ce qui pointe vers quoi (c'est-à-dire vos données stockées à votre représentation de données dans le code). Vous avez déjà fait cette partie, et ce n'est pas ce qui est automatisé.

0
Charles Lambert