Je commence juste avec les tests unitaires et TDD en général. J’ai essayé avant mais maintenant je suis déterminé à l’append à mon stream de travail et à écrire de meilleurs logiciels.
Hier, j’ai posé une question de ce genre, mais cela semble être une question en soi. Je me suis assis pour commencer à mettre en œuvre une classe de service que je vais utiliser pour extraire la logique métier des contrôleurs et mapper vers des modèles et des interactions de données spécifiques en utilisant EF6.
Le problème est que je me suis déjà bloqué parce que je ne voulais pas faire abstraction de EF dans un repository (il sera toujours disponible en dehors des services pour des requêtes spécifiques, etc.) et je voudrais tester mes services (le contexte EF sera utilisé) .
Je suppose que la question est la suivante: y a-t-il un intérêt à le faire? Si oui, comment les gens le font-ils à la lumière des abstractions qui fuient provoquées par IQueryable et des nombreux messages de Ladislav Mrnka sur le fait que les tests unitaires ne sont pas simples à cause des différences entre les fournisseurs Linq? mise en œuvre telle qu’elle est associée à une firebase database spécifique.
Le code que je veux tester semble assez simple. (c’est juste du code factice pour essayer de comprendre ce que je fais, je veux piloter la création avec TDD)
Le contexte
public interface IContext { IDbSet Products { get; set; } IDbSet Categories { get; set; } int SaveChanges(); } public class DataContext : DbContext, IContext { public IDbSet Products { get; set; } public IDbSet Categories { get; set; } public DataContext(ssortingng connectionSsortingng) : base(connectionSsortingng) { } }
Un service
public class ProductService : IProductService { private IContext _context; public ProductService(IContext dbContext) { _context = dbContext; } public IEnumerable GetAll() { var query = from p in _context.Products select p; return query; } }
Actuellement, je pense à faire certaines choses:
Y a-t-il quelqu’un là-bas qui fait ça là-bas sans repo et avec succès?
C’est un sujet qui m’intéresse beaucoup. Il y a beaucoup de puristes qui disent que vous ne devriez pas tester des technologies telles que EF et NHibernate. Ils ont raison, ils ont déjà été testés de manière très rigoureuse et, comme indiqué précédemment, il est souvent inutile de passer beaucoup de temps à tester ce que vous ne possédez pas.
Cependant, vous possédez la firebase database ci-dessous! C’est là que cette approche se décompose à mon avis, vous n’avez pas besoin de tester qu’EF / NH fait son travail correctement. Vous devez tester que vos mappages / implémentations fonctionnent avec votre firebase database. À mon avis, c’est l’une des parties les plus importantes d’un système que vous pouvez tester.
À ssortingctement parler, nous sortons du domaine des tests unitaires et des tests d’intégration, mais les principes restnt les mêmes.
La première chose à faire est de pouvoir simuler votre DAL afin que votre BLL puisse être testée indépendamment de EF et SQL. Ce sont vos tests unitaires. Ensuite, vous devez concevoir vos tests d’intégration pour prouver votre DAL, à mon avis, ils sont tout aussi importants.
Il y a plusieurs choses à considérer:
Il existe deux approches principales pour configurer votre firebase database, la première consiste à exécuter un script de création de firebase database UnitTest. Cela garantit que votre firebase database de test unitaire sera toujours dans le même état au début de chaque test (vous pouvez soit le réinitialiser, soit exécuter chaque test dans une transaction pour vous en assurer).
Votre autre option est ce que je fais, exécutez des configurations spécifiques pour chaque test individuel. Je crois que c’est la meilleure approche pour deux raisons principales:
Malheureusement, votre compromis est la vitesse. Il faut du temps pour exécuter tous ces tests, pour exécuter tous ces scripts de configuration / suppression.
Un dernier point, il peut être très difficile d’écrire une si grande quantité de SQL pour tester votre ORM. C’est là que je prends une très mauvaise approche (les puristes ici seront en désaccord avec moi). J’utilise mon ORM pour créer mon test! Plutôt que d’avoir un script séparé pour chaque test DAL dans mon système, j’ai une phase de configuration de test qui crée les objects, les attache au contexte et les enregistre. Je lance ensuite mon test.
C’est loin d’être la solution idéale, mais dans la pratique, je trouve que c’est BEAUCOUP plus facile à gérer (surtout lorsque vous avez plusieurs milliers de tests), sinon vous créez un grand nombre de scripts. Praticité sur la pureté.
Je reviendrai sans aucun doute sur cette réponse dans quelques années (mois / jours) et je ne suis pas d’accord avec moi car mes approches ont changé – mais c’est mon approche actuelle.
Pour essayer de résumer tout ce que j’ai dit plus haut, voici mon test d’intégration de firebase database:
[Test] public void LoadUser() { this.RunTest(session => // the NH/EF session to attach the objects to { var user = new UserAccount("Mr", "Joe", "Bloggs"); session.Save(user); return user.UserID; }, id => // the ID of the entity we need to load { var user = LoadMyUser(id); // load the entity Assert.AreEqual("Mr", user.Title); // test your properties Assert.AreEqual("Joe", user.Firstname); Assert.AreEqual("Bloggs", user.Lastname); } }
L’essentiel à noter ici est que les sessions des deux boucles sont complètement indépendantes. Dans votre implémentation de RunTest, vous devez vous assurer que le contexte est validé et détruit et que vos données ne peuvent provenir que de votre firebase database pour la deuxième partie.
Modifier le 13/10/2014
J’ai dit que je réviserais probablement ce modèle au cours des prochains mois. Bien que je maintienne en grande partie l’approche que j’ai préconisée ci-dessus, j’ai légèrement mis à jour mon mécanisme de test. J’ai maintenant tendance à créer les entités dans TestSetup et TestTearDown.
[SetUp] public void Setup() { this.SetupTest(session => // the NH/EF session to attach the objects to { var user = new UserAccount("Mr", "Joe", "Bloggs"); session.Save(user); this.UserID = user.UserID; }); } [TearDown] public void TearDown() { this.TearDownDatabase(); }
Ensuite, testez chaque propriété individuellement
[Test] public void TestTitle() { var user = LoadMyUser(this.UserID); // load the entity Assert.AreEqual("Mr", user.Title); } [Test] public void TestFirstname() { var user = LoadMyUser(this.UserID); Assert.AreEqual("Joe", user.Firstname); } [Test] public void TestLastname() { var user = LoadMyUser(this.UserID); Assert.AreEqual("Bloggs", user.Lastname); }
Plusieurs raisons expliquent cette approche:
Je pense que cela rend la classe de test plus simple et les tests plus granulaires (les assertions uniques sont bonnes )
Modifier le 5/3/2015
Une autre révision de cette approche. Bien que les configurations de niveau classe soient très utiles pour des tests tels que les propriétés de chargement, elles sont moins utiles lorsque les différentes configurations sont requirejses. Dans ce cas, la création d’une nouvelle classe pour chaque cas est excessive.
Pour aider avec cela, j’ai maintenant tendance à avoir deux classes de base SetupPerTest
et SingleSetup
. Ces deux classes exposent le framework comme requirejs.
Dans le SingleSetup
nous avons un mécanisme très similaire à celui décrit dans ma première édition. Un exemple serait
public TestProperties : SingleSetup { public int UserID {get;set;} public override DoSetup(ISession session) { var user = new User("Joe", "Bloggs"); session.Save(user); this.UserID = user.UserID; } [Test] public void TestLastname() { var user = LoadMyUser(this.UserID); // load the entity Assert.AreEqual("Bloggs", user.Lastname); } [Test] public void TestFirstname() { var user = LoadMyUser(this.UserID); Assert.AreEqual("Joe", user.Firstname); } }
Cependant, les références garantissant que seules les entités correctes sont chargées peuvent utiliser une approche SetupPerTest
public TestProperties : SetupPerTest { [Test] public void EnsureCorrectReferenceIsLoaded() { int friendID = 0; this.RunTest(session => { var user = CreateUserWithFriend(); session.Save(user); friendID = user.Friends.Single().FriendID; } () => { var user = GetUser(); Assert.AreEqual(friendID, user.Friends.Single().FriendID); }); } [Test] public void EnsureOnlyCorrectFriendsAreLoaded() { int userID = 0; this.RunTest(session => { var user = CreateUserWithFriends(2); var user2 = CreateUserWithFriends(5); session.Save(user); session.Save(user2); userID = user.UserID; } () => { var user = GetUser(userID); Assert.AreEqual(2, user.Friends.Count()); }); } }
En résumé, les deux approches fonctionnent en fonction de ce que vous essayez de tester.
Expérience d’effort
Après de nombreuses lectures, j’ai utilisé Effort dans mes tests: pendant les tests, le Context est construit par une fabrique qui retourne une version en mémoire, ce qui me permet de tester une ardoise vierge à chaque fois. En dehors des tests, la fabrique est résolue en une qui renvoie le contexte complet.
Cependant, j’ai l’impression que les tests effectués sur une base complète de la firebase database ont tendance à faire baisser les tests; vous vous rendez compte que vous devez vous occuper de toute une série de dépendances pour tester une partie du système. Vous avez aussi tendance à dériver vers l’organisation de tests qui peuvent ne pas être liés, simplement parce qu’il n’y a qu’un seul object énorme qui gère tout. Si vous ne faites pas attention, vous pourriez avoir à faire des tests d’intégration plutôt que des tests unitaires.
J’aurais préféré tester contre quelque chose de plus abstrait plutôt qu’un énorme DBContext, mais je ne pouvais pas trouver le bon point entre les tests significatifs et les tests de base. Répondez à mon inexpérience.
Donc, je trouve l’effort intéressant; Si vous avez besoin de démarrer rapidement, c’est un bon outil pour démarrer rapidement et obtenir des résultats. Cependant, je pense que quelque chose d’un peu plus élégant et abstrait devrait être la prochaine étape et c’est ce que je vais étudier ensuite. Favoriser cet article pour voir où il va ensuite 🙂
Modifier pour append : Il faut un certain temps pour se réchauffer, alors vous regardez environ. 5 secondes au démarrage du test. Cela peut être un problème pour vous si vous voulez que votre suite de tests soit très efficace.
Édité pour clarification:
J’ai utilisé Effort pour tester une application Webservice. Chaque message M qui entre est acheminé vers un IHandlerOf
via Windsor. Castle.Windsor résout le IHandlerOf
qui récupère les dépendances du composant. Une de ces dépendances est le DataContextFactory
, qui permet au gestionnaire de demander l’usine.
Dans mes tests, j’instancie directement le composant IHandlerOf, me moque de tous les sous-composants du SUT et gère le DataContextFactory
dans l’ DataContextFactory
vers le gestionnaire.
Cela signifie que je ne teste pas les unités au sens ssortingct, car la firebase database est touchée par mes tests. Cependant, comme je l’ai dit ci-dessus, cela m’a permis de commencer à tester rapidement certains points de l’application.
Si vous souhaitez créer un code de test, vous devez isoler votre code que vous souhaitez tester (dans ce cas, votre service) à partir de ressources externes (par exemple, des bases de données). Vous pourriez probablement faire cela avec une sorte de fournisseur EF en mémoire , mais une manière beaucoup plus commune est d’abstraire votre implémentation EF, par exemple avec une sorte de modèle de référentiel. Sans cet isolement, les tests que vous écrivez seront des tests d’intégration, pas des tests unitaires.
Comme pour le test du code EF – J’écris des tests d’intégration automatisés pour mes référentiels qui écrivent diverses lignes dans la firebase database lors de leur initialisation, puis appellent mes implémentations pour s’assurer qu’elles se comportent correctement (par exemple qu’ils sont sortingés dans le bon ordre).
Il s’agit de tests d’intégration et non de tests unitaires, car les tests reposent sur la présence d’une connexion de firebase database et que la firebase database cible dispose déjà du dernier schéma mis à jour.
Je ne voudrais pas un code de test que je ne possède pas. Que testez-vous ici, que le compilateur MSFT fonctionne?
Cela dit, pour rendre ce code testable, vous devez presque faire en sorte que votre couche d’access aux données soit distincte de votre code de logique métier. Ce que je fais, c’est prendre tout mon matériel EF et le mettre dans une classe (ou plusieurs) DAO ou DAL qui possède également une interface correspondante. Ensuite, j’écris mon service qui contiendra l’object DAO ou DAL injecté en tant que dépendance (de préférence l’injection constructeur) référencée en tant qu’interface. Maintenant, la partie à tester (votre code) peut facilement être testée en masquant l’interface DAO et en l’injectant dans votre instance de service dans votre test unitaire.
//this is testable just inject a mock of IProductDAO during unit testing public class ProductService : IProductService { private IProductDAO _productDAO; public ProductService(IProductDAO productDAO) { _productDAO = productDAO; } public List GetAllProducts() { return _productDAO.GetAll(); } ... }
Je considère que les couches d’access aux données en direct font partie des tests d’intégration et non des tests unitaires. J’ai vu des gars faire des vérifications sur le nombre de voyages à la firebase database en veille prolongée, mais ils étaient sur un projet impliquant des milliards d’enregistrements dans leur magasin de données et ces voyages supplémentaires comptaient vraiment.
J’ai tâtonné quelque temps pour atteindre ces considérations:
1- Si mon application accède à la firebase database, pourquoi pas le test? Que se passe-t-il si quelque chose ne va pas avec l’access aux données? Les tests doivent le connaître au préalable et m’avertir du problème.
2- Le Repository Pattern est un peu difficile et prend du temps.
Donc, je suis venu avec cette approche, que je ne pense pas être la meilleure, mais a répondu à mes attentes:
Use TransactionScope in the tests methods to avoid changes in the database.
Pour ce faire, il faut:
1- Installez EntityFramework dans le projet de test. 2- Placez la chaîne de connexion dans le fichier app.config de Test Project. 3- Référencez le dll System.Transactions dans le projet de test.
L’effet secondaire unique est que la graine d’identité va s’incrémenter en essayant d’insérer, même lorsque la transaction est annulée. Mais comme les tests sont effectués sur une firebase database de développement, cela ne devrait pas poser de problème.
Exemple de code:
[TestClass] public class NameValueTest { [TestMethod] public void Edit() { NameValueController controller = new NameValueController(); using(var ts = new TransactionScope()) { Assert.IsNotNull(controller.Edit(new Models.NameValue() { NameValueId = 1, name1 = "1", name2 = "2", name3 = "3", name4 = "4" })); //no complete, automatically abort //ts.Complete(); } } [TestMethod] public void Create() { NameValueController controller = new NameValueController(); using (var ts = new TransactionScope()) { Assert.IsNotNull(controller.Create(new Models.NameValue() { name1 = "1", name2 = "2", name3 = "3", name4 = "4" })); //no complete, automatically abort //ts.Complete(); } } }
Donc, voici la chose, Entity Framework est une implémentation et malgré le fait qu’il résume la complexité de l’interaction de la firebase database, l’interaction directe rest un couplage étroit et c’est pourquoi il est difficile de tester.
Le test d’unité consiste à tester la logique d’une fonction et chacun de ses résultats potentiels indépendamment de toute dépendance externe, dans ce cas, le magasin de données. Pour ce faire, vous devez être en mesure de contrôler le comportement du magasin de données. Par exemple, si vous souhaitez affirmer que votre fonction renvoie false si l’utilisateur récupéré ne répond pas à un ensemble de critères, votre magasin de données [simulé] doit être configuré pour toujours renvoyer un utilisateur ne répondant pas aux critères, et vice versa. inversement pour l’affirmation opposée.
Cela dit, et acceptant le fait que EF est une implémentation, je privilégierais probablement l’idée d’abstraire un référentiel. Semble un peu redondant? Ce n’est pas le cas, car vous résolvez un problème qui isole votre code de l’implémentation des données.
Dans DDD, les référentiels ne renvoient que des racines agrégées, pas DAO. De cette manière, le consommateur du référentiel n’a jamais à connaître l’implémentation des données (ce qui ne devrait pas être le cas) et nous pouvons l’utiliser comme exemple pour résoudre ce problème. Dans ce cas, l’object généré par EF est un DAO et, en tant que tel, doit être masqué à votre application. Ceci est un autre avantage du référentiel que vous définissez. Vous pouvez définir un object métier en tant que type de retour à la place de l’object EF. Ce que fait le repository est maintenant de masquer les appels à EF et de mapper la réponse EF à cet object métier défini dans la signature de repos. Maintenant, vous pouvez utiliser ce repository à la place de la dépendance DbContext que vous injectez dans vos classes et, par conséquent, vous pouvez désormais simuler cette interface pour vous donner le contrôle dont vous avez besoin pour tester votre code de manière isolée.
Il y a un peu plus de travail et beaucoup de malentendu, mais cela résout un vrai problème. Il y a un fournisseur de mémoire qui a été mentionné dans une réponse différente qui pourrait être une option (je ne l’ai pas essayé), et son existence même est la preuve de la nécessité de la pratique.
Je ne suis pas du tout d’accord avec la meilleure réponse, car elle évite le problème réel qui consiste à isoler votre code, puis à essayer de tester votre application. Bien sûr, testez votre application si vous le souhaitez, mais résolvez le problème ici et obtenez une couverture de code réelle.
En bref, je dirais non, le jus ne vaut pas la peine de tester une méthode de service avec une seule ligne qui récupère les données du modèle. Dans mon expérience, les nouveaux venus sur TDD veulent tester absolument tout. L’ancien châtaigne consistant à extraire une façade dans un cadre tiers pour pouvoir créer une maquette de cette API avec laquelle vous bâtissez / étendez-vous afin que vous puissiez injecter des données factices n’a que peu d’intérêt pour moi. Tout le monde a un sharepoint vue différent sur la qualité des tests unitaires. J’ai tendance à être plus pragmatique ces jours-ci et à me demander si mon test ajoute vraiment de la valeur au produit final et à quel prix.
Je souhaite partager une approche commentée et brièvement discutée, mais montrer un exemple concret que j’utilise actuellement pour aider à tester les services basés sur EF.
Tout d’abord, j’aimerais utiliser le fournisseur de mémoire d’EF Core, mais il s’agit d’EF 6. En outre, pour d’autres systèmes de stockage comme RavenDB, je serais également partisan de tests via le fournisseur de firebase database en mémoire. Encore une fois – ceci est spécifiquement pour aider à tester le code basé sur EF sans beaucoup de cérémonie .
Voici les objectives que j’ai eu lors de l’élaboration d’un motif:
Je suis d’accord avec les déclarations précédentes selon lesquelles EF est toujours un détail d’implémentation et il est correct de penser que vous avez besoin de l’abstraire pour effectuer un test unitaire “pur”. Je conviens également que, idéalement, je voudrais faire en sorte que le code EF fonctionne – mais cela implique une firebase database sandbox, un fournisseur de mémoire, etc. Mon approche résout les deux problèmes – tests d’intégration pour tester spécifiquement votre code EF.
La façon dont je suis arrivé à cela était simplement en encapsulant le code EF dans des classes de requêtes et de commandes dédiées. L’idée est simple: encapsulez tout code EF dans une classe et dépendez d’une interface dans les classes qui l’auraient utilisé à l’origine. Le principal problème que je devais résoudre consistait à éviter d’append de nombreuses dépendances aux classes et de définir beaucoup de code dans mes tests.
C’est là qu’intervient une bibliothèque simple et utile: Mediatr . Il permet une messagerie simple dans le processus et le fait en découplant les “requêtes” des gestionnaires qui implémentent le code. Cela a un avantage supplémentaire de découpler le “quoi” du “comment”. Par exemple, en encapsulant le code EF en petits blocs, vous pouvez remplacer les implémentations par un autre fournisseur ou par un mécanisme totalement différent, car tout ce que vous faites consiste à envoyer une requête pour effectuer une action.
En utilisant l’dependency injection (avec ou sans cadre – votre préférence), nous pouvons facilement simuler le médiateur et contrôler les mécanismes de demande / réponse pour activer le code EF de test unitaire.
Tout d’abord, disons que nous avons un service qui doit être testé avec une logique métier:
public class FeatureService { private readonly IMediator _mediator; public FeatureService(IMediator mediator) { _mediator = mediator; } public async Task ComplexBusinessLogic() { // resortingeve relevant objects var results = await _mediator.Send(new GetRelevantDbObjectsQuery()); // normally, this would have looked like... // var results = _myDbContext.DbObjects.Where(x => foo).ToList(); // perform business logic // ... } }
Commencez-vous à voir les avantages de cette approche? Non seulement vous encapsulez explicitement tout le code lié à l’EF dans des classes descriptives, mais vous autorisez l’extensibilité en supprimant le problème de “comment” cette requête est traitée – cette classe ne se soucie pas si les objects pertinents proviennent de EF, MongoDB, ou un fichier texte.
Maintenant, pour la demande et le gestionnaire, via MediatR:
public class GetRelevantDbObjectsQuery : IRequest { // no input needed for this particular request, // but you would simply add plain properties here if needed } public class GetRelevantDbObjectsEFQueryHandler : IRequestHandler { private readonly IDbContext _db; public GetRelevantDbObjectsEFQueryHandler(IDbContext db) { _db = db; } public DbObject[] Handle(GetRelevantDbObjectsQuery message) { return _db.DbObjects.Where(foo => bar).ToList(); } }
Comme vous pouvez le voir, l’abstraction est simple et encapsulée. Il est également absolument testable car dans un test d’intégration, vous pouvez tester cette classe individuellement.
Alors, à quoi ressemble un test unitaire de notre service de fonctionnalités? C’est simple Dans ce cas, j’utilise Moq pour me moquer (utilisez ce qui vous rend heureux):
[TestClass] public class FeatureServiceTests { // mock of Mediator to handle request/responses private Mock _mediator; // subject under test private FeatureService _sut; [TestInitialize] public void Setup() { // set up Mediator mock _mediator = new Mock (MockBehavior.Ssortingct); // inject mock as dependency _sut = new FeatureService(_mediator.Object); } [TestCleanup] public void Teardown() { // ensure we have called or expected all calls to Mediator _mediator.VerifyAll(); } [TestMethod] public void ComplexBusinessLogic_Does_What_I_Expect() { var dbObjects = new List() { // set up any test objects new DbObject() { } }; // arrange // setup Mediator to return our fake objects when it receives a message to perform our query // in practice, I find it better to create an extension method that encapsulates this setup here _mediator.Setup(x => x.Send(It.IsAny(), default(CancellationToken)).ReturnsAsync(dbObjects.ToArray()).Callback( (GetRelevantDbObjectsQuery message, CancellationToken token) => { // using Moq Callback functionality, you can make assertions // on expected request being passed in Assert.IsNotNull(message); }); // act _sut.ComplexBusinessLogic(); // assertions } }
Vous pouvez voir que tout ce dont nous avons besoin est une configuration unique et nous n’avons même pas besoin de configurer quoi que ce soit – c’est un test unitaire très simple. Soyons clairs: il est tout à fait possible de se passer de quelque chose comme Mediatr (vous devez simplement implémenter une interface et la IGetRelevantDbObjectsQuery
pour des tests, par exemple IGetRelevantDbObjectsQuery
), mais en pratique pour une base de code volumineuse avec de nombreuses fonctionnalités et innate DI soutien offres Mediatr.
Si vous vous demandez comment j’organise ces cours, c’est assez simple:
- MyProject - Features - MyFeature - Queries - Commands - Services - DependencyConfig.cs (Ninject feature modules)
L’organisation par tranches de fonctionnalités n’est pas à l’ordre du jour, mais cela garde tous les codes pertinents / dépendants ensemble et facilement détectables. Plus important encore, je sépare les requêtes et les commandes – en suivant le principe de séparation commande / requête .
Cela répond à tous mes critères: c’est la cérémonie basse, c’est facile à comprendre et il y a des avantages cachés supplémentaires. Par exemple, comment gérez-vous les modifications de sauvegarde? Maintenant, vous pouvez simplifier votre contexte Db en utilisant une interface de rôle ( IUnitOfWork.SaveChangesAsync()
) et des appels simulés à l’interface de rôle unique ou vous pouvez encapsuler la validation / restitution dans vos RequestHandlers. , tant que c’est maintenable. Par exemple, j’ai été tenté de créer une requête / un gestionnaire générique unique dans lequel vous pouviez simplement passer un object EF et l’enregistrer / mettre à jour / le supprimer – mais vous devez vous demander votre intention et vous souvenir que si vous voulez remplacez le gestionnaire par un autre fournisseur / implémentation de stockage, vous devez probablement créer des commandes / requêtes explicites représentant ce que vous avez l’intention de faire. More often than not, a single service or feature will need something specific–don’t create generic stuff before you have a need for it.
There are of course caveats to this pattern–you can go too far with a simple pub/sub mechanism. I’ve limited my implementation to only abstracting EF-related code, but adventurous developers could start using MediatR to go overboard and message-ize everything–something good code review practices and peer reviews should catch. That’s a process issue, not an issue with MediatR, so just be cognizant of how you’re using this pattern.
You wanted a concrete example of how people are unit testing/mocking EF and this is an approach that’s working successfully for us on our project–and the team is super happy with how easy it is to adopt. J’espère que ça aide! As with all things in programming, there are multiple approaches and it all depends on what you want to achieve. I value simplicity, ease of use, maintainability, and discoverability–and this solution meets all those demands.
I like to separate my filters from other portions of the code and test those as I outline on my blog here http://coding.grax.com/2013/08/testing-custom-linq-filter-operators.html
That being said, the filter logic being tested is not identical to the filter logic executed when the program is run due to the translation between the LINQ expression and the underlying query language, such as T-SQL. Still, this allows me to validate the logic of the filter. I don’t worry too much about the translations that happen and things such as case-sensitivity and null-handling until I test the integration between the layers.
There is Effort which is an in memory entity framework database provider. I’ve not actually sortinged it… Haa just spotted this was mentioned in the question!
Alternatively you could switch to EntityFrameworkCore which has an in memory database provider built-in.
https://github.com/tamasflamich/effort
I used a factory to get a context, so i can create the context close to its use. This seems to work locally in visual studio but not on my TeamCity build server, not sure why yet.
return new MyContext(@"Server=(localdb)\mssqllocaldb;Database=EFProviders.InMemory;Trusted_Connection=True;");