Ich habe diese Antwort von Ragzitsu für die gleiche Frage gelesen. Ich bin immer noch verwirrt, wie man Dinge umsetzt. Kann mir jemand ein Beispiel für eine Implementierung geben.
Ich habe folgende Kurse:
class Fizz : IFizz
{
}
class Buzz : IBuzz
{
}
class Bar : IBar
{
}
class Foo : IFoo
{
public Foo(IBar bar, IFizz fizz, IBuzz buzz)
{
//initialize etc.
}
//public methods
}
Wie kann man den Konstrukteur hier praktisch umgehen? Ich möchte so etwas tun
var foo = new Mock<IFoo>();
Mit anderen Worten, wie würde der Code nach dem Rat aussehen?
The best thing to do would be right click on your class and choose Extract interface.
Sie können einen Mock erstellen, bei dem der Konstruktor über Parameterargumente verfügt, indem Sie auf MockBehavior verweisen (siehe unten)
Mock<testClass>(MockBehavior.Strict, new object[] {"Hello"});
Es ist unklar, was Sie wirklich versuchen.
Wenn Sie das Interface bereits definiert haben IFoo
(was Ihre Frage zu zeigen scheint) und Sie es mit einem anderen Typ verspotten müssen, sind Sie bereits festgelegt: var bazz = new Bazz(new Mock<IFoo>());
Wenn Sie eine konkrete Klasse Foo
haben, die Sie nicht in eine Schnittstelle abstrahiert haben, die Sie als Abhängigkeit für einen anderen Typ bereitstellen müssen, ist das Extrahieren einer Schnittstelle eine der gebräuchlichsten Methoden, um die Initialisierung von zu ermöglichen ein Bazz
-Objekt, ohne eine tatsächliche Foo
-Klasse zu erstellen. Abhängig davon, wie Ihr tatsächlicher Code aussieht, können Sie den Umfang einschränken, indem Sie nur die Teile von IFoo
in Foo
abstrahieren, von denen Bazz
abhängt. Die andere Möglichkeit besteht darin, den Beton zu verspotten und moq mit den zu verwendenden Konstruktor-Argumenten zu versehen. Sie müssen sicherstellen, dass die Abhängigkeitsteile von Foo
"Mock-fähig" (öffentlich virtuell) sind, um den Mock mit Ihrem Setup und Restuls zu versorgen.
Als redigieren Sie, um die Frage von wirklich zu beantworten
wie würde der Code nach dem Rat aussehen? Das Beste, was zu tun ist, ist ... Schnittstelle extrahieren?
Der Rat, eine Schnittstelle zu extrahieren, ist möglicherweise nicht genau das, wonach Sie suchen. Wie bereits in anderen Antworten erwähnt, sind wir nicht sicher, was Sie tatsächlich testen möchten .
Wenn Sie versuchen, - Funktionalität auf der konkreten Foo
-Implementierung von IFoo
zu testen, dann extrahieren Sie eine Schnittstelle und verspotten IFoo
wird Ihnen nicht wirklich helfen, da moq die Schnittstelle selbst implementieren wird und nur die "Funktionalität" bereitstellt, die Sie in den Setup-Methoden und Eigenschaften von angeben diese Implementierung. Wenn Sie dies tun, rufen [~ # ~] und [~ # ~] die zu testende Methode eine andere Methode in der konkreten Foo
-Implementierung auf , [~ # ~] und [~ # ~] diese Methode ist etwas, für das Sie eine Abstraktion bereitstellen möchten, dann würde ich als @Chris Marisic erwähnte und die Methode Sie ändern Wunsch, zu virtual
zu abstrahieren. Mit diesem als virtuell verwenden Sie können ein spöttisches Framework, um die Abstraktion bereitzustellen, oder tun, was ich in der Vergangenheit getan habe, und erstellen einen Untertyp aus dem Testobjekt in der Testklasse.
public class MyFooTests
{
... // Tests
private class SubFoo : Foo
{
public override void MethodToAbstract(bool par)
{
// do something expected
}
}
}
Mit dieser Methode müssen Sie dem Konstruktor von Foo noch etwas zur Verfügung stellen, aber das wäre ein Hauptfall für die Verwendung von Mocks auf diesen Schnittstellen.
Wenn Sie nun Foo
testen und die zu testende Methode nur Methoden für IBar
, IFizz
, IBuzz
aufruft, ist dies der Fall Wenn Sie keine andere Abstraktion erstellen möchten, sind Sie bereits eingerichtet. Verspotten Sie diese Schnittstellen, richten Sie sie so ein, dass sie das zurückgeben, was Sie in einem bestimmten Testfall erwarten, und versehen Sie Foo
mit den Verspottungsobjekten.
Die Ratschläge zum Extrakt-Interface sind wirklich nützlich, wenn Ihr Testobjekt von Foo abhängig ist, wie von @Chris Marisic erwähnt. Ich kann jedoch seine Ansicht nicht teilen, dass ich in dieser Situation keine Schnittstellen schaffen möchte. Es hängt davon ab, was für Sie mehr riecht: Erstellen einer Schnittstelle, um die Möglichkeit zum Verspotten bereitzustellen, oder Ändern des Modifikators, um dieselbe bereitzustellen. Wenn Sie den Modifikator ändern und dennoch moq verwenden, um das konkrete Foo
zu verspotten, müssen Sie entweder einen Standardkonstruktor ( für mich ziemlich übel riechend verfügbar machen =) oder erstellen Sie den Mock mit den angegebenen Konstruktorargumenten, wie in Ihrer referenzierten Antwort erwähnt.
Nehmen wir an, Sie haben Folgendes:
public class Baz
{
private Foo foo;
public Baz(Foo inputFoo)
{
this.foo = inputFoo;
}
public bool GetFromTheFoo()
{
// other stuff here
var fooStuff = this.foo.GetStuff();
// logic on the stuff;
return fooStuff != null;
}
}
Im obigen ist Baz
abhängig von Foo
. Nach dem Extrahieren einer Schnittstelle von Foo
müsste Baz
IFoo
akzeptieren.
Dann würde Foo
so aussehen wie in Ihrer Frage und IFoo
würde eine Signaturdefinition für die Methode haben, die Sie von Baz
abstrahieren möchten.
object GetStuff();
Jetzt würden Sie in Ihrem Test immer noch var foo = new Mock<IFoo>();
verwenden und foo
Ihrem Baz
Testobjekt geben. Vergessen Sie nicht, die Methode IFoo.GetStuff()
einzurichten.
foo.Setup(f => f.GetStuff()).Returns(new object());
Da Sie nun die Abstraktion IFoo
erstellt haben, ist dies nicht erforderlich
hier um den Konstruktor herumkommen?
weil der Mock von IFoo
nur einen von moq bereitgestellten Standardkonstruktor hat.
Ich persönlich bevorzuge den Schnittstellenansatz, wenn er angewendet wird, weil er IFizz
, IBuzz
und IBar
aus der Abhängigkeitskette von Baz
entfernt. Wenn Baz
von Foo
und Foo
von IFizz etc.
Abhängt, dann hängt Baz
auch von IFizz etc.
Nach dem Extrahieren von IFoo
hängt Baz
nur von IFoo
ab.
Ich weiß, dass dies vor einiger Zeit gefragt wurde und vielleicht keinen Wert für mich hat, dies jetzt hinzuzufügen, aber für zukünftige Leser sollten wir nicht glauben, dass die folgenden Dinge bei der Verwendung von moq dasselbe sind:
var fooDouble = new Mock<IFoo>();
ist nicht dasselbe wie:
var fooDouble = new Mock<Foo>();
Ich verstehe, dass beim Erstellen eines Mock of IFoo das moq-Framework eine Doppelklasse generiert, die die IFoo-Schnittstelle implementiert und somit den Standardkonstruktor für sich selbst in der generierten Implementierung bereitstellt. Der eigentliche Foo-Mock implementiert die Schnittstelle jedoch nicht direkt im generierten Code, sondern erbt von der konkreten Foo-Klasse und erfordert daher, dass alle Methoden, die gestubbt werden sollen, virtuell gemacht werden, und ob es konkrete Implementierungen gibt, die Sie möchten Wenn Sie das zu testende Gerät tatsächlich verwenden möchten, benötigen Sie "CallBase = true" für Ihr Modell. Wenn Sie dies mit einer konkreten Klasse mit Konstruktorargumenten tun möchten, suchen Sie möglicherweise nach der Antwort von @ Palkin, um zu vermeiden, dass ein Standardkonstruktor verfügbar gemacht wird.
Sie sollten nichts ändern, wenn Sie eine Schnittstelle IFoo
haben und die Klasse Foo
verspotten möchten, die einen Konstruktor mit Parametern hat.
Ihr Code ist genau das, was Sie brauchen.
var foo = new Mock<IFoo>();
Der folgende Hinweis behandelt die Situationen, in denen die Klasse keine Schnittstelle und keinen parameterlosen Konstruktor hat. Tatsächlich können Sie alle benötigten Parameter an den Konstruktor von Mock
übergeben.
var mock = new Mock<IFoo>("constructor", "arguments");
aber
"Am besten klicken Sie mit der rechten Maustaste auf Ihre Klasse und wählen Schnittstelle extrahieren." (c)
Am besten klicken Sie mit der rechten Maustaste auf Ihre Klasse und wählen Sie Schnittstelle extrahieren.
Ich werde dieses Konzept in einem orthogonalen Sinn ansprechen. Ich bin mit dieser Aussage nicht einverstanden, eine Schnittstelle ist nicht die Lösung zur fraglichen Situation.
Zurück zum Text der vorherigen Frage:
public class CustomerSyncEngine {
public CustomerSyncEngine(ILoggingProvider loggingProvider,
ICrmProvider crmProvider,
ICacheProvider cacheProvider) { ... }
public void MethodWithDependencies() {
loggingProvider.Log();
crmProvider.Crm();
cacheProvider.Cache();
}
}
Beachten Sie die Methode, die ich hinzugefügt habe.
Ich glaube, die eigentliche Frage ist, wenn Sie nicht speziell CustomerSyncEngine
testen, sondern stattdessen eine Klasse testen, die abhängig von CustomerSyncEngine
ist. Nennen wir diese Klasse SuperSyncEngine
. Einen Test gegen SuperSyncEngine
zu erstellen, wird mühsam, da Sie das gesamte CustomerSyncEngine
mit seinen 3 Schnittstellen sowie alle weiteren Abhängigkeiten, die SuperSyncEngine
hat, verspotten müssen.
Da der Code, den Sie testen möchten, SuperSyncEngine
ist, der von CustomerSyncEngine
abhängt, ist eine Schnittstelle hier nicht die Antwort. Sie könnten ICustomerSyncEngine
erstellen, aber diese Schnittstelle sollte nicht nur für ein spöttisches Framework erstellt werden. Die bessere Lösung ist, CustomerSyncEngine.MethodWithDependencies
In virtuell zu ändern
public virtual void MethodWithDependencies() {
loggingProvider.Log();
crmProvider.Crm();
cacheProvider.Cache();
}
Auf diese Weise können Sie die Methode durch ein spöttisches Framework ersetzen, das die Abhängigkeiten ignoriert, mit denen CustomerSyncEngine
geliefert wird.
Wenn Sie diesem Ansatz folgen, benötigen Sie wahrscheinlich einen Standardkonstruktor, der auf CustomerSyncEngine
verfügbar gemacht wird, damit er verspottet werden kann. Es ist möglich, dass Sie das umgehen und die Abhängigkeiten mit null oder anderen Werten erfüllen, aber das wäre zusätzliche Arbeit, wenn das Ziel darin besteht, die Reibung zu verringern.
Dies ist eine Art alter Beitrag, aber ich bin gerade auf ein ähnliches Problem gestoßen (beginnend mit Moq). Falls jemand anderes ein ähnliches Problem hat, hier ist was ich hatte:
class Bar : IBar
{
}
class Foo : IFoo
{
public Foo(IBar bar)
{
//initialize etc.
}
//public methods
}
class Manager : IManager
{
public Manager(Foo foo)
{
//initialize etc
}
}
Was ich versuche zu tun, ist Test Manager
nicht Foo
.
Hier war mein erster Testcode, der einen Fehler verursachte.
[TestFixture]
public class ManagerTest
{
[Test]
public void SomeTest()
{
var fooMock = Mock<IFoo>();
var managerUnderTest = new Manager(fooMock.Object);
}
}
Der Fehler ist Castle.DynamicProxy.InvalidProxyConstructorArgumentsException : Can not instantiate proxy of class: Something.Models.Foo. Could not find a parameterless constructor.
Beim Lesen der Fehlermeldung versteht Moq nicht, wie man Foo instanziiert, da es keinen parameterlosen Konstruktor gibt und wir Moq nicht sagen, wie man einen mit Parametern instanziiert. Ändern Sie diesen zweiten Abschnitt in:
[TestFixture]
public class ManagerTest
{
[Test]
public void SomeTest()
{
var barMock = Mock<IBar>();
var fooMock = Mock<IFoo>(barMock.Object);
var managerUnderTest = new Manager(fooMock.Object);
//proceed with test
}
}
Wenn du deine FOo-Klasse testest, musst du dich nicht lustig machen. Sie müssen nur die Klassen verspotten, die von der Klasse abhängen, die Sie testen möchten.
Etwas wie:
Mock<IBar> bar = new Mock<IBar>();
Mock<IBuzz> buzz = new Mock<IBuzz>();
Mock<IFizz> fizz= new Mock<IFizz>();
Foo foo = new Foo(bar.Object, buzz.Object, fizz.Object);
Rufe dann die Methode in foo auf, die du testen willst;)
Wenn die Methode in foo eine Methode in bar/fuzz oder fizz verwendet, sollten Sie die Sintax wie folgt verwenden:
buzz.Setup(x => x.DoSomething()).Returns(1);
Auf diese Weise ruft Ihre foo-Methode beim Aufruf das DoSomething auf und gibt immer 1 zurück;)
Schnittstellen sind nützlich, wenn ein Typ mehr als eine Implementierung hat. Negativ bedeutet dies, dass Sie darauf achten sollten, keine Schnittstellen abzuleiten, nur weil Sie Schnittstellen möchten (häufiger Fehler). Positiv zu vermerken ist, dass die verspottete Schnittstelle per Definition die zweite Implementierung ist.
Die Schlussfolgerung lautet also: Wenn ein Typ als Abhängigkeit eines anderen Typs fungiert, ist es ein guter Kandidat, eine Schnittstelle zu implementieren. In diesem Fall können Sie die Schnittstelle in Unit-Tests frei und vollständig verspotten.
Achten Sie beim Definieren einer Schnittstelle darauf, dass Sie der Schnittstelle nur funktionale Teile hinzufügen: Methoden, die definieren, wie das Objekt funktioniert und nicht wie es sieht aus. Eine Schnittstelle mit einer Reihe von Gettern/Setzern bietet keinen Mehrwert für das Design. Dies ist ein ziemlich großer theoretischer Bereich, und dieses winzige Fenster ist nicht der richtige Ort, um mehr darüber zu schreiben.
Um den Zusammenhang mit Ihrer Frage zu klären: Eine verspottete Implementierung sollte das für die Schnittstelle erforderliche Verhalten bieten. Dazu verwenden Sie Funktionen des Mocking-Frameworks. Dies hat nichts mit der konkreten Implementierung der Foo-Klasse zu tun - Sie definieren das spezifische Verhalten des Mock-Objekts.