Effort - FirstOrDefault renvoie null lorsque Faking Database


Question

J'essaie de créer des tests unitaires pour mon projet. Après des recherches approfondies, j'ai trouvé Effort. L'idée est géniale. Elle se moque de la base de données au lieu de simuler le contexte DBContext, ce qui est d'ailleurs très difficile à comprendre. en utilisant un schéma complexe.

Cependant, j'essaie d'obtenir le courrier électronique d'un utilisateur après l'avoir spécifiquement ajouté à la base de données en mémoire créée par Effort. Voici le code

MyContext contextx = new MyContext(Effort.DbConnectionFactory.CreateTransient());

var client = new Client
{
    ClientId = 2,
    PersonId = 3,
    Person = new Person
    {
        PersonId = 3,
        EMail = "xxxxx@gmail.com"
    }
};
contextx.Client.Add(client); //<-- client got added, I checked it and is there

var email = contextx.Client.Select(c => c.Person.EMail).FirstOrDefault(); 

Dans la dernière ligne ci-dessus, je ne peux pas renvoyer l'e-mail xxxx@gmail.com. Il renvoie toujours null.

Des idées?

Réponse acceptée

Répondre à votre question directe

Pour la question spécifique que vous avez posée, je suggérerais deux choses:

  1. Jetez un coup d'œil à contextx.Client.ToArray() et voyez combien de membres vous avez réellement dans cette collection. Il se peut que la collection du Client soit réellement vide, auquel cas vous obtiendrez null. Ou bien, le premier élément de la collection Client peut avoir une valeur null pour EMail .

  2. Comment le comportement change-t-il si vous appelez contextx.SaveChanges() avant d'interroger la collection Client sur DbContext? Je suis curieux de voir si l'appel de SaveChanges entraînera la SaveChanges la nouvelle valeur insérée dans la collection. Cela ne devrait vraiment pas être nécessaire, mais il pourrait y avoir une étrange interaction entre Effort et DbContext .

EDIT: SaveChanges() s’avère être la réponse.

Suggestions d'essais générales

Puisque vous avez coché cette question avec la balise "tests unitaires", je vais vous donner quelques conseils généraux sur les tests unitaires basés sur mes dix années de pratique et d’entraînement. Le test unitaire consiste à tester plusieurs petites parties de votre application de manière isolée. Cela signifie généralement que les tests unitaires n'interagissent qu'avec quelques classes à la fois. Cela signifie également que les tests unitaires ne doivent pas dépendre de bibliothèques ou de dépendances externes (telles que la base de données). Inversement, un test d' intégration exerce simultanément plusieurs parties du système et peut avoir des dépendances externes sur des éléments tels que des bases de données.

Bien que cela puisse sembler un problème de terminologie, les termes sont importants pour communiquer l’intention réelle de vos tests à d’autres membres de votre équipe.

Dans ce cas, soit vous voulez vraiment tester à l’unité une fonctionnalité qui dépend de DbContext, soit vous essayez de tester votre couche d’accès aux données. Si vous essayez d'écrire un test unitaire isolé de quelque chose qui dépend directement de DbContext, vous devez rompre la dépendance à l'égard de DbContext. J'expliquerai cela ci-dessous dans Briser la dépendance sur DbContext ci-dessous. Sinon, vous essayez réellement d’intégrer votre test à DbContext, y compris la façon dont vos entités sont mappées. Dans ce cas, j'ai toujours jugé préférable d'isoler ces tests et d'utiliser une vraie base de données (locale). Vous voudrez probablement utiliser une base de données installée localement de la même variété que celle que vous utilisez en production. SqlExpress fonctionne souvent très bien. Pointez vos tests sur une instance de la base de données que les tests peuvent complètement détruire. Laissez vos tests supprimer toutes les données existantes avant d'exécuter chaque test. Ensuite, ils peuvent configurer toutes les données dont ils ont besoin sans craindre un conflit entre les données existantes.

Briser la dépendance sur DbContext

Alors, comment écrivez-vous de bons tests unitaires lorsque votre logique métier dépend de l'accès à DbContext ? Vous pas.

Dans mes applications qui utilisent Entity Framework pour la persistance des données, je m'assure que l'accès à DbContext est contenu dans un projet d'accès aux données distinct. En règle générale, je vais créer des classes qui implémentent le modèle Repository et ces classes sont autorisées à prendre une dépendance de DbContext . Donc, dans ce cas, je ClientRepository un ClientRepository qui implémente une interface IClientRepository . L'interface ressemblerait à quelque chose comme ça:

public interface IClientRepository {

    Client GetClientByEMail(string email);

}

Ensuite, toutes les classes nécessitant un accès à la méthode peuvent être testées à l’aide d’un module stub / mock / what de base. Rien ne doit s'inquiéter de se moquer de DbContext . Votre couche d'accès aux données est contenue et vous pouvez la tester de manière approfondie en utilisant une base de données réelle. Pour des suggestions sur la façon de tester votre couche d'accès aux données, voir ci-dessus.

En outre, la mise en œuvre de cette interface définit ce que signifie rechercher un Client par adresse électronique dans un lieu unique et unifié. L'interface IClientRepository vous permet de répondre rapidement à la question "Comment interroge-t-on les entités Client dans notre système?"

Prendre une dépendance sur DbContext correspond à peu près à la même problématique que le fait de permettre aux classes de domaine de créer une dépendance à la chaîne de connexion et d’avoir du code ADO.Net partout. Cela signifie que vous devez créer un véritable magasin de données (même avec une fausse base de données) contenant de vraies données. Toutefois, si vous DbContext votre accès à DbContext dans un ensemble d'accès aux données spécifique, vous constaterez que vos tests unitaires sont beaucoup plus faciles à écrire.

En ce qui concerne l'organisation de projet, j'autorise généralement uniquement mon projet d'accès aux données à faire référence à Entity Framework. J'aurai un projet principal séparé dans lequel je définirai les entités. Je définirai également les interfaces d'accès aux données du projet Core. Ensuite, les implémentations d’interface concrètes sont intégrées au projet d’accès aux données. La plupart des projets de votre solution peuvent alors simplement prendre une dépendance sur le projet principal et seul l'exécutable de niveau supérieur ou le projet Web doit réellement dépendre du projet d'accès aux données.





Sous licence: CC-BY-SA
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi