Ich fange gerade mit Unit-Tests und TDD im Allgemeinen an. Ich habe mich schon einmal versucht, aber jetzt bin ich entschlossen, es meinem Workflow hinzuzufügen und bessere Software zu schreiben.
Ich habe gestern eine Frage gestellt, die dies beinhaltete, aber es scheint eine Frage für sich zu sein. Ich habe mich hingesetzt, um mit der Implementierung einer Serviceklasse zu beginnen, mit der ich die Geschäftslogik von den Controllern abstrahieren und mithilfe von EF6 bestimmten Modellen und Dateninteraktionen zuordnen kann.
Das Problem ist, dass ich mich bereits blockiert habe, weil ich EF nicht in einem Repository abstrahieren wollte (es wird weiterhin außerhalb der Dienste für bestimmte Abfragen usw. verfügbar sein) und meine Dienste testen möchte (EF-Kontext wird verwendet). .
Hier ist wohl die Frage, gibt es einen Grund, dies zu tun? Wenn ja, wie machen es die Leute in freier Wildbahn angesichts der undichten Abstraktionen, die von IQueryable verursacht werden, und der vielen großartigen Beiträge von Ladislav Mrnka zum Thema Unit-Tests, die aufgrund der Unterschiede bei den Linq-Anbietern bei der Arbeit mit einem In-Memory nicht einfach sind Implementierung gemäß einer bestimmten Datenbank.
Der Code, den ich testen möchte, scheint ziemlich einfach zu sein. (Dies ist nur Dummy-Code, um zu verstehen, was ich tue. Ich möchte die Erstellung mit TDD vorantreiben.)
Kontext
public interface IContext
{
IDbSet<Product> Products { get; set; }
IDbSet<Category> Categories { get; set; }
int SaveChanges();
}
public class DataContext : DbContext, IContext
{
public IDbSet<Product> Products { get; set; }
public IDbSet<Category> Categories { get; set; }
public DataContext(string connectionString)
: base(connectionString)
{
}
}
Bedienung
public class ProductService : IProductService
{
private IContext _context;
public ProductService(IContext dbContext)
{
_context = dbContext;
}
public IEnumerable<Product> GetAll()
{
var query = from p in _context.Products
select p;
return query;
}
}
Derzeit bin ich in der Einstellung, ein paar Dinge zu tun:
- Verspotten des EF-Kontexts mit so etwas wie diesem Ansatz - Verspotten des EF beim Unit-Test oder direktes Verwenden eines Mocking-Frameworks auf der Schnittstelle wie moq - Nehmen Sie den Schmerz, den die Unit-Tests möglicherweise bestehen, aber nicht unbedingt durchgängig arbeiten, und sichern Sie sie mit Integrationstests?
- Vielleicht etwas wie Effort verwenden , um EF zu verspotten - ich habe es nie benutzt und bin mir nicht sicher, ob es jemand anderes in freier Wildbahn benutzt?
- Sie müssen nichts testen, was einfach zu EF zurückruft. Im Wesentlichen werden Servicemethoden, die EF direkt aufrufen (getAll usw.), nicht auf Einheiten getestet, sondern nur auf Integration getestet?
Jemand da draußen, der das tatsächlich ohne Repo macht und Erfolg hat?
Antworten:
Dies ist ein Thema, das mich sehr interessiert. Es gibt viele Puristen, die sagen, dass Sie Technologien wie EF und NHibernate nicht testen sollten. Sie haben Recht, sie sind bereits sehr streng getestet und wie in einer früheren Antwort angegeben, ist es oft sinnlos, viel Zeit damit zu verbringen, zu testen, was Sie nicht besitzen.
Sie besitzen jedoch die Datenbank darunter! Hier bricht dieser Ansatz meiner Meinung nach zusammen. Sie müssen nicht testen, ob EF / NH ihre Arbeit korrekt ausführen. Sie müssen testen, ob Ihre Zuordnungen / Implementierungen mit Ihrer Datenbank funktionieren. Meiner Meinung nach ist dies einer der wichtigsten Teile eines Systems, das Sie testen können.
Genau genommen bewegen wir uns jedoch aus dem Bereich des Unit-Tests in den Bereich des Integrationstests, aber die Prinzipien bleiben dieselben.
Das erste, was Sie tun müssen, ist, Ihre DAL verspotten zu können, damit Ihre BLL unabhängig von EF und SQL getestet werden kann. Dies sind Ihre Unit-Tests. Als nächstes müssen Sie Ihre Integrationstests entwerfen , um Ihre DAL zu beweisen. Meiner Meinung nach sind diese genauso wichtig.
Es gibt ein paar Dinge zu beachten:
Es gibt zwei Hauptansätze zum Einrichten Ihrer Datenbank: Der erste besteht darin, ein UnitTest-Skript zum Erstellen einer Datenbank auszuführen. Dadurch wird sichergestellt, dass sich Ihre Komponententestdatenbank zu Beginn jedes Tests immer im selben Status befindet (Sie können dies entweder zurücksetzen oder jeden Test in einer Transaktion ausführen, um dies sicherzustellen).
Ihre andere Option ist, was ich tue, spezifische Setups für jeden einzelnen Test auszuführen. Ich glaube, dies ist aus zwei Hauptgründen der beste Ansatz:
Leider ist Ihr Kompromiss hier Geschwindigkeit. Es braucht Zeit, um alle diese Tests auszuführen und alle diese Setup- / Teardown-Skripte auszuführen.
Ein letzter Punkt: Es kann sehr schwierig sein, eine so große Menge an SQL zu schreiben, um Ihr ORM zu testen. Hier gehe ich sehr böse vor (die Puristen hier werden mir nicht zustimmen). Ich benutze mein ORM, um meinen Test zu erstellen! Anstatt für jeden DAL-Test in meinem System ein separates Skript zu haben, habe ich eine Test-Setup-Phase, in der die Objekte erstellt, an den Kontext angehängt und gespeichert werden. Ich führe dann meinen Test durch.
Dies ist alles andere als die ideale Lösung, aber in der Praxis finde ich, dass es viel einfacher zu verwalten ist (insbesondere wenn Sie mehrere tausend Tests haben), da Sie sonst eine große Anzahl von Skripten erstellen. Praktikabilität über Reinheit.
Ich werde ohne Zweifel in ein paar Jahren (Monaten / Tagen) auf diese Antwort zurückblicken und mit mir selbst nicht einverstanden sein, da sich meine Ansätze geändert haben - dies ist jedoch mein aktueller Ansatz.
Um alles zusammenzufassen, was ich oben gesagt habe, ist dies mein typischer DB-Integrationstest:
Das Wichtigste dabei ist, dass die Sitzungen der beiden Schleifen völlig unabhängig sind. Bei der Implementierung von RunTest müssen Sie sicherstellen, dass der Kontext festgeschrieben und zerstört wird und Ihre Daten nur für den zweiten Teil aus Ihrer Datenbank stammen können.
Bearbeiten 13/10/2014
Ich habe gesagt, dass ich dieses Modell wahrscheinlich in den kommenden Monaten überarbeiten werde. Während ich weitgehend zu dem oben vertretenen Ansatz stehe, habe ich meinen Testmechanismus leicht aktualisiert. Ich neige jetzt dazu, die Entitäten in TestSetup und TestTearDown zu erstellen.
Testen Sie dann jede Eigenschaft einzeln
Für diesen Ansatz gibt es mehrere Gründe:
Ich denke, dies macht die Testklasse einfacher und die Tests detaillierter ( einzelne Aussagen sind gut )
Bearbeiten 03.05.2015
Eine weitere Überarbeitung dieses Ansatzes. Setups auf Klassenebene sind zwar sehr hilfreich für Tests wie das Laden von Eigenschaften, aber weniger nützlich, wenn die verschiedenen Setups erforderlich sind. In diesem Fall ist das Einrichten einer neuen Klasse für jeden Fall übertrieben.
Um dies zu unterstützen, habe ich jetzt tendenziell zwei Basisklassen
SetupPerTest
undSingleSetup
. Diese beiden Klassen legen das Framework nach Bedarf offen.In der haben
SingleSetup
wir einen sehr ähnlichen Mechanismus wie in meiner ersten Bearbeitung beschrieben. Ein Beispiel wäreReferenzen, die sicherstellen, dass nur die richtigen Entites geladen werden, können jedoch einen SetupPerTest-Ansatz verwenden
Zusammenfassend funktionieren beide Ansätze je nachdem, was Sie testen möchten.
quelle
Effort Experience Feedback hier
Nach vielem Lesen habe ich Effort in meinen Tests verwendet: Während der Tests wird der Kontext von einer Factory erstellt, die eine In-Memory-Version zurückgibt, mit der ich jedes Mal gegen eine leere Tafel testen kann. Außerhalb der Tests wird die Factory in eine aufgelöst, die den gesamten Kontext zurückgibt.
Ich habe jedoch das Gefühl, dass das Testen gegen ein voll funktionsfähiges Modell der Datenbank dazu neigt, die Tests nach unten zu ziehen. Sie erkennen, dass Sie sich darum kümmern müssen, eine ganze Reihe von Abhängigkeiten einzurichten, um einen Teil des Systems zu testen. Sie tendieren auch dazu, Tests zusammenzustellen, die möglicherweise nicht miteinander zusammenhängen, nur weil es nur ein großes Objekt gibt, das alles handhabt. Wenn Sie nicht aufpassen, führen Sie möglicherweise Integrationstests anstelle von Komponententests durch
Ich hätte es vorgezogen, gegen etwas Abstrakteres als gegen einen riesigen DBContext zu testen, aber ich konnte den Sweet Spot zwischen aussagekräftigen Tests und Bare-Bone-Tests nicht finden. Kreide es bis zu meiner Unerfahrenheit.
Also finde ich Effort interessant; Wenn Sie sofort loslegen müssen, ist dies ein gutes Werkzeug, um schnell loszulegen und Ergebnisse zu erzielen. Ich denke jedoch, dass etwas eleganteres und abstrakteres der nächste Schritt sein sollte, und das werde ich als nächstes untersuchen. Diesen Beitrag favorisieren, um zu sehen, wohin er als nächstes geht :)
Bearbeiten, um hinzuzufügen : Das Aufwärmen dauert einige Zeit. 5 Sekunden beim Teststart. Dies kann ein Problem für Sie sein, wenn Ihre Testsuite sehr effizient sein soll.
Zur Verdeutlichung bearbeitet:
Ich habe Effort verwendet, um eine Webservice-App zu testen. Jede eingegebene Nachricht M wird
IHandlerOf<M>
über Windsor an a weitergeleitet. Castle.Windsor löst das aufIHandlerOf<M>
, wodurch die Abhängigkeiten der Komponente aufgelöst werden. Eine dieser Abhängigkeiten ist dieDataContextFactory
, mit der der Handler nach der Factory fragen kannIn meinen Tests instanziiere ich die IHandlerOf-Komponente direkt, verspotte alle Unterkomponenten des SUT und verarbeite den Aufwand
DataContextFactory
für den Handler.Dies bedeutet, dass ich keinen Unit-Test im engeren Sinne durchführe, da die DB von meinen Tests betroffen ist. Wie ich oben sagte, konnte ich jedoch sofort loslegen und einige Punkte in der Anwendung schnell testen
quelle
Wenn Sie Unit- Test-Code verwenden möchten, müssen Sie Ihren zu testenden Code (in diesem Fall Ihren Service) von externen Ressourcen (z. B. Datenbanken) isolieren. Sie könnten dies wahrscheinlich mit einer Art In-Memory-EF-Anbieter tun. Eine weitaus häufigere Methode besteht jedoch darin, Ihre EF-Implementierung zu abstrahieren, z. B. mit einer Art Repository-Muster. Ohne diese Isolation sind alle Tests, die Sie schreiben, Integrationstests, keine Komponententests.
Was das Testen von EF-Code betrifft - Ich schreibe automatisierte Integrationstests für meine Repositorys, die während ihrer Initialisierung verschiedene Zeilen in die Datenbank schreiben, und rufe dann meine Repository-Implementierungen auf, um sicherzustellen, dass sie sich wie erwartet verhalten (z. B. um sicherzustellen, dass die Ergebnisse korrekt gefiltert werden, oder dass sie in der richtigen Reihenfolge sortiert sind).
Hierbei handelt es sich um Integrationstests, nicht um Komponententests, da für die Tests eine Datenbankverbindung erforderlich ist und in der Zieldatenbank bereits das neueste aktuelle Schema installiert ist.
quelle
Hier ist also die Sache, Entity Framework ist eine Implementierung. Trotz der Tatsache, dass es die Komplexität der Datenbankinteraktion abstrahiert, ist die direkte Interaktion immer noch eine enge Kopplung, und deshalb ist das Testen verwirrend.
Beim Unit-Test geht es darum, die Logik einer Funktion und jedes ihrer potenziellen Ergebnisse isoliert von externen Abhängigkeiten zu testen, in diesem Fall dem Datenspeicher. Dazu müssen Sie das Verhalten des Datenspeichers steuern können. Wenn Sie beispielsweise behaupten möchten, dass Ihre Funktion false zurückgibt, wenn der abgerufene Benutzer bestimmte Kriterien nicht erfüllt, sollte Ihr [verspotteter] Datenspeicher so konfiguriert sein, dass immer ein Benutzer zurückgegeben wird, der die Kriterien nicht erfüllt, und umgekehrt umgekehrt für die gegenteilige Behauptung.
Vor diesem Hintergrund und angesichts der Tatsache, dass EF eine Implementierung ist, würde ich wahrscheinlich die Idee der Abstraktion eines Repositorys befürworten. Scheint ein bisschen überflüssig? Dies ist nicht der Fall, da Sie ein Problem lösen, bei dem Ihr Code von der Datenimplementierung isoliert wird.
In DDD geben die Repositorys immer nur aggregierte Roots zurück, nicht DAO. Auf diese Weise muss der Verbraucher des Repositorys nie über die Datenimplementierung Bescheid wissen (wie es nicht sein sollte), und wir können dies als Beispiel für die Lösung dieses Problems verwenden. In diesem Fall ist das von EF generierte Objekt ein DAO und sollte daher vor Ihrer Anwendung ausgeblendet werden. Dies ist ein weiterer Vorteil des von Ihnen definierten Repositorys. Sie können ein Geschäftsobjekt anstelle des EF-Objekts als Rückgabetyp definieren. Das Repo versteckt nun die Aufrufe von EF und ordnet die EF-Antwort dem in der Reposignatur definierten Geschäftsobjekt zu. Jetzt können Sie dieses Repo anstelle der DbContext-Abhängigkeit verwenden, die Sie in Ihre Klassen einfügen, und folglich können Sie diese Schnittstelle verspotten, um Ihnen die Kontrolle zu geben, die Sie benötigen, um Ihren Code isoliert zu testen.
Es ist ein bisschen mehr Arbeit und viele schnüffeln mit der Nase daran, aber es löst ein echtes Problem. Es gibt einen In-Memory-Anbieter, der in einer anderen Antwort erwähnt wurde, die eine Option sein könnte (ich habe es nicht ausprobiert), und seine Existenz ist ein Beweis für die Notwendigkeit der Praxis.
Ich bin mit der Top-Antwort überhaupt nicht einverstanden, da sie das eigentliche Problem, das darin besteht, Ihren Code zu isolieren, umgeht und sich dann mit dem Testen Ihrer Zuordnung befasst. Testen Sie auf jeden Fall Ihr Mapping, wenn Sie möchten, aber gehen Sie hier auf das eigentliche Problem ein und erhalten Sie eine echte Codeabdeckung.
quelle
Ich würde keinen Unit-Test-Code verwenden, den ich nicht besitze. Was testen Sie hier, damit der MSFT-Compiler funktioniert?
Um diesen Code testbar zu machen, MÜSSEN Sie jedoch Ihre Datenzugriffsschicht fast von Ihrem Geschäftslogikcode trennen. Ich nehme alle meine EF-Sachen und stelle sie in eine (oder mehrere) DAO- oder DAL-Klasse, die auch eine entsprechende Schnittstelle hat. Dann schreibe ich meinen Dienst, in den das DAO- oder DAL-Objekt als Abhängigkeit (vorzugsweise Konstruktorinjektion) als Schnittstelle eingefügt wird. Jetzt kann der zu testende Teil (Ihr Code) einfach getestet werden, indem Sie die DAO-Schnittstelle verspotten und diese in Ihre Service-Instanz innerhalb Ihres Unit-Tests einfügen.
Ich würde Live-Datenzugriffsschichten als Teil des Integrationstests betrachten, nicht als Komponententest. Ich habe gesehen, wie Leute zuvor überprüft haben, wie viele Fahrten in den Datenbank-Ruhezustand unternommen wurden, aber sie waren an einem Projekt beteiligt, bei dem Milliarden von Datensätzen in ihrem Datenspeicher enthalten waren, und diese zusätzlichen Fahrten waren wirklich wichtig.
quelle
Ich habe irgendwann herumgefummelt, um diese Überlegungen zu erreichen:
1- Wenn meine Anwendung auf die Datenbank zugreift, warum sollte der Test nicht? Was ist, wenn beim Datenzugriff etwas nicht stimmt? Die Tests müssen es vorher wissen und mich auf das Problem aufmerksam machen.
2- Das Repository-Muster ist etwas schwierig und zeitaufwändig.
Also habe ich mir diesen Ansatz ausgedacht, den ich nicht für den besten halte, der aber meine Erwartungen erfüllt hat:
Dazu ist es notwendig:
1- Installieren Sie das EntityFramework im Testprojekt. 2- Fügen Sie die Verbindungszeichenfolge in die Datei app.config von Test Project ein. 3- Verweisen Sie auf die DLL System.Transactions in Test Project.
Der einzigartige Nebeneffekt besteht darin, dass der Identitätsstart beim Einfügen erhöht wird, selbst wenn die Transaktion abgebrochen wird. Da die Tests jedoch anhand einer Entwicklungsdatenbank durchgeführt werden, sollte dies kein Problem sein.
Beispielcode:
quelle
Kurz gesagt, ich würde nein sagen, der Saft ist es nicht wert, eine Servicemethode mit einer einzelnen Zeile zu testen, die Modelldaten abruft. Nach meiner Erfahrung wollen TDD-Neulinge absolut alles testen. Die alte Kastanie, eine Fassade zu einem Framework eines Drittanbieters zu abstrahieren, nur damit Sie ein Modell dieser Framework-API erstellen können, mit der Sie bastardisieren / erweitern, damit Sie Dummy-Daten einfügen können, ist für mich von geringem Wert. Jeder hat eine andere Ansicht darüber, wie viel Unit-Test am besten ist. Ich bin heutzutage eher pragmatisch und frage mich, ob und zu welchem Preis mein Test wirklich einen Mehrwert für das Endprodukt darstellt.
quelle
Ich möchte einen kommentierten und kurz diskutierten Ansatz teilen, aber ein aktuelles Beispiel zeigen, das ich derzeit verwende, um beim Testen von EF-basierten Diensten zu helfen .
Zunächst würde ich gerne den In-Memory-Anbieter von EF Core verwenden, aber hier geht es um EF 6. Darüber hinaus wäre ich für andere Speichersysteme wie RavenDB auch ein Befürworter des Testens über den In-Memory-Datenbankanbieter. Auch dies dient speziell dazu, EF-basierten Code ohne viel Zeremonie zu testen .
Hier sind die Ziele, die ich hatte, als ich ein Muster entwickelte:
Ich stimme früheren Aussagen zu, dass EF immer noch ein Implementierungsdetail ist und es in Ordnung ist, das Gefühl zu haben, dass Sie es abstrahieren müssen, um einen "reinen" Komponententest durchzuführen. Ich stimme auch zu, dass ich im Idealfall sicherstellen möchte, dass der EF-Code selbst funktioniert - dies beinhaltet jedoch eine Sandbox-Datenbank, einen In-Memory-Anbieter usw. Mein Ansatz löst beide Probleme - Sie können EF-abhängigen Code sicher testen und erstellen Integrationstests zum spezifischen Testen Ihres EF-Codes.
Dies wurde durch einfaches Einkapseln von EF-Code in dedizierte Abfrage- und Befehlsklassen erreicht. Die Idee ist einfach: Binden Sie einfach einen EF-Code in eine Klasse ein und hängen Sie von einer Schnittstelle in den Klassen ab, die ihn ursprünglich verwendet hätten. Das Hauptproblem, das ich lösen musste, bestand darin, zu vermeiden, dass Klassen zahlreiche Abhängigkeiten hinzugefügt und in meinen Tests viel Code eingerichtet wurden.
Hier kommt eine nützliche, einfache Bibliothek ins Spiel : Mediatr . Es ermöglicht einfaches In-Process-Messaging und entkoppelt "Anforderungen" von den Handlern, die den Code implementieren. Dies hat den zusätzlichen Vorteil, dass das "Was" vom "Wie" entkoppelt wird. Wenn Sie beispielsweise den EF-Code in kleine Blöcke kapseln, können Sie die Implementierungen durch einen anderen Anbieter oder einen völlig anderen Mechanismus ersetzen, da Sie lediglich eine Anforderung zum Ausführen einer Aktion senden.
Mithilfe der Abhängigkeitsinjektion (mit oder ohne Framework - Ihre Präferenz) können wir den Mediator leicht verspotten und die Anforderungs- / Antwortmechanismen steuern, um das Testen von EF-Code zu ermöglichen.
Nehmen wir zunächst an, wir haben einen Service mit Geschäftslogik, die wir testen müssen:
Beginnen Sie, den Nutzen dieses Ansatzes zu erkennen? Sie kapseln nicht nur den gesamten EF-bezogenen Code explizit in beschreibende Klassen, sondern ermöglichen auch die Erweiterbarkeit, indem Sie die Bedenken hinsichtlich der Implementierung dieser Anforderung beseitigen. Dieser Klasse ist es egal, ob die relevanten Objekte von EF, MongoDB, stammen. oder eine Textdatei.
Nun zur Anfrage und Bearbeitung über MediatR:
Wie Sie sehen können, ist die Abstraktion einfach und gekapselt. Es ist auch absolut prüfbar , weil in einem Integrationstest, Sie könnten diese Klasse einzeln testen - es gibt keine Geschäftsinteressen in hier gemischt.
Wie sieht ein Unit-Test unseres Feature-Service aus? Es ist ganz einfach. In diesem Fall benutze ich Moq, um mich zu verspotten (benutze, was dich glücklich macht):
Sie können sehen, dass wir nur ein einziges Setup benötigen und nicht einmal zusätzliche Einstellungen vornehmen müssen - es ist ein sehr einfacher Komponententest. Lassen Sie uns klar sein: Dies ist völlig möglich ohne etwas wie Mediatr (Sie würden einfach eine Schnittstelle implementieren und sie für Tests verspotten, z. B.
IGetRelevantDbObjectsQuery
), aber in der Praxis für eine große Codebasis mit vielen Funktionen und Abfragen / Befehlen liebe ich die Kapselung und angeborene DI-Unterstützung, die Mediatr anbietet.Wenn Sie sich fragen, wie ich diese Kurse organisiere, ist das ziemlich einfach:
Das Organisieren nach Feature-Slices ist nebensächlich, aber dies hält den gesamten relevanten / abhängigen Code zusammen und ist leicht erkennbar. Am wichtigsten ist, dass ich die Abfragen gegen Befehle trenne - nach dem Prinzip der Befehls- / Abfragetrennung .
Dies erfüllt alle meine Kriterien: Es ist eine niedrige Zeremonie, es ist leicht zu verstehen und es gibt zusätzliche versteckte Vorteile. Wie gehen Sie beispielsweise mit dem Speichern von Änderungen um? Jetzt können Sie Ihren Datenbankkontext mithilfe einer Rollenschnittstelle vereinfachen (
IUnitOfWork.SaveChangesAsync()
) und Scheinaufrufe an die Einzelrollenschnittstelle oder Sie können das Festschreiben / Zurücksetzen in Ihren RequestHandlern kapseln - Sie möchten dies jedoch lieber tun, solange es wartbar ist. Zum Beispiel war ich versucht, einen einzelnen generischen Anforderungs- / Handler zu erstellen, bei dem Sie nur ein EF-Objekt übergeben und es speichern / aktualisieren / entfernen würden - aber Sie müssen fragen, was Ihre Absicht ist, und sich daran erinnern, wenn Sie möchten Wenn Sie den Handler gegen einen anderen Speicheranbieter / eine andere Implementierung austauschen, sollten Sie wahrscheinlich explizite Befehle / Abfragen erstellen, die das darstellen, was Sie beabsichtigen. In den meisten Fällen benötigt ein einzelner Dienst oder eine einzelne Funktion etwas Bestimmtes. Erstellen Sie keine allgemeinen Inhalte, bevor Sie sie benötigen.Dieses Muster weist natürlich einige Einschränkungen auf - Sie können mit einem einfachen Pub / Sub-Mechanismus zu weit gehen. Ich habe meine Implementierung darauf beschränkt, nur EF-bezogenen Code zu abstrahieren, aber abenteuerlustige Entwickler könnten MediatR verwenden, um über Bord zu gehen und alles mit Nachrichten zu versehen - etwas, das gute Codeüberprüfungspraktiken und Peer-Reviews erreichen sollten. Dies ist ein Prozessproblem, kein Problem mit MediatR. Seien Sie sich also bewusst, wie Sie dieses Muster verwenden.
Sie wollten ein konkretes Beispiel dafür, wie Menschen EF testen / verspotten, und dies ist ein Ansatz, der für uns bei unserem Projekt erfolgreich funktioniert - und das Team ist sehr zufrieden damit, wie einfach es zu übernehmen ist. Ich hoffe das hilft! Wie bei allen Dingen in der Programmierung gibt es mehrere Ansätze, und alles hängt davon ab, was Sie erreichen möchten. Ich schätze Einfachheit, Benutzerfreundlichkeit, Wartbarkeit und Auffindbarkeit - und diese Lösung erfüllt all diese Anforderungen.
quelle
Es gibt Effort, einen Datenbankanbieter für In-Memory-Entity-Frameworks. Ich habe es nicht wirklich versucht ... Haa hat gerade entdeckt, dass dies in der Frage erwähnt wurde!
Alternativ können Sie zu EntityFrameworkCore wechseln, in dem ein In-Memory-Datenbankanbieter integriert ist.
https://blog.goyello.com/2016/07/14/save-time-mocking-use-your-real-entity-framework-dbcontext-in-unit-tests/
https://github.com/tamasflamich/effort
Ich habe eine Factory verwendet, um einen Kontext zu erhalten, damit ich den Kontext in der Nähe seiner Verwendung erstellen kann. Dies scheint lokal in Visual Studio zu funktionieren, aber nicht auf meinem TeamCity-Build-Server. Ich weiß noch nicht warum.
quelle
Ich möchte meine Filter von anderen Teilen des Codes trennen und diese testen, wie ich in meinem Blog hier skizziere: http://coding.grax.com/2013/08/testing-custom-linq-filter-operators.html
Allerdings ist die getestete Filterlogik aufgrund der Übersetzung zwischen dem LINQ-Ausdruck und der zugrunde liegenden Abfragesprache wie T-SQL nicht identisch mit der Filterlogik, die beim Ausführen des Programms ausgeführt wird. Auf diese Weise kann ich die Logik des Filters überprüfen. Ich mache mir keine allzu großen Sorgen über die Übersetzungen und Dinge wie Groß- und Kleinschreibung und Nullbehandlung, bis ich die Integration zwischen den Ebenen teste.
quelle
Es ist wichtig zu testen, was das Entity Framework erwartet (dh Ihre Erwartungen zu validieren). Eine Möglichkeit, dies erfolgreich zu tun, ist die Verwendung von moq wie in diesem Beispiel gezeigt (zu lang, um in diese Antwort zu kopieren):
https://docs.microsoft.com/en-us/ef/ef6/fundamentals/testing/mocking
Seien Sie jedoch vorsichtig ... Es wird nicht garantiert, dass ein SQL-Kontext Dinge in einer bestimmten Reihenfolge zurückgibt, es sei denn, Sie haben ein geeignetes "OrderBy" in Ihrer Linq-Abfrage, sodass es möglich ist, Dinge zu schreiben, die beim Testen mit einer In-Memory-Liste erfolgreich sind ( linq-to-entity), schlägt jedoch in Ihrer uat / live-Umgebung fehl, wenn (linq-to-sql) verwendet wird.
quelle