Ich habe kürzlich mit einigen Freunden diskutiert, welche der folgenden 2 Methoden am besten geeignet ist, um Ergebnisse oder Aufrufe von Methoden innerhalb derselben Klasse von Methoden innerhalb derselben Klasse zurückzugeben.
Dies ist ein sehr vereinfachtes Beispiel. In Wirklichkeit sind die Funktionen viel komplexer.
Beispiel:
public class MyClass
{
public bool FunctionA()
{
return FunctionB() % 2 == 0;
}
protected int FunctionB()
{
return new Random().Next();
}
}
Um dies zu testen, haben wir 2 Methoden.
Methode 1: Verwenden Sie Funktionen und Aktionen, um die Funktionalität der Methoden zu ersetzen. Beispiel:
public class MyClass
{
public Func<int> FunctionB { get; set; }
public MyClass()
{
FunctionB = FunctionBImpl;
}
public bool FunctionA()
{
return FunctionB() % 2 == 0;
}
protected int FunctionBImpl()
{
return new Random().Next();
}
}
[TestClass]
public class MyClassTests
{
private MyClass _subject;
[TestInitialize]
public void Initialize()
{
_subject = new MyClass();
}
[TestMethod]
public void FunctionA_WhenNumberIsOdd_ReturnsTrue()
{
_subject.FunctionB = () => 1;
var result = _subject.FunctionA();
Assert.IsFalse(result);
}
}
Methode 2: machen Sie Mitglieder virtuell, leiten Sie Klasse ab, und verwenden Sie in abgeleiteter Klasse Funktionen und Aktionen, um Funktionalität zu ersetzen Beispiel:
public class MyClass
{
public bool FunctionA()
{
return FunctionB() % 2 == 0;
}
protected virtual int FunctionB()
{
return new Random().Next();
}
}
public class TestableMyClass
{
public Func<int> FunctionBFunc { get; set; }
public MyClass()
{
FunctionBFunc = base.FunctionB;
}
protected override int FunctionB()
{
return FunctionBFunc();
}
}
[TestClass]
public class MyClassTests
{
private TestableMyClass _subject;
[TestInitialize]
public void Initialize()
{
_subject = new TestableMyClass();
}
[TestMethod]
public void FunctionA_WhenNumberIsOdd_ReturnsTrue()
{
_subject.FunctionBFunc = () => 1;
var result = _subject.FunctionA();
Assert.IsFalse(result);
}
}
Ich möchte wissen, was besser ist und auch WARUM?
Update: HINWEIS: FunctionB kann auch öffentlich sein
c#
design
unit-testing
tranceru1
quelle
quelle
FunctionA
Gibt einen Bool zurück, setzt aber nur eine lokale Variablex
und gibt nichts zurück.public static
nur in einer anderen Klasse befinden.FunctionB
ist defekt durch Design.new Random().Next()
ist fast immer falsch. Sie sollten die Instanz von injizierenRandom
. (Random
ist auch eine schlecht gestaltete Klasse, die ein paar zusätzliche Probleme verursachen kann)Antworten:
Nach Original-Poster-Update bearbeitet.
Haftungsausschluss: Kein C # -Programmierer (hauptsächlich Java oder Ruby). Meine Antwort wäre: Ich würde es überhaupt nicht testen, und ich denke nicht, dass Sie sollten.
Die längere Version lautet: Private / Protected-Methoden sind nicht Teil der API. Sie stellen im Grunde genommen Implementierungsoptionen dar, die Sie überprüfen, aktualisieren oder vollständig verwerfen können, ohne dass dies Auswirkungen auf die Außenwelt hat.
Ich nehme an, Sie haben einen Test für FunctionA (), den Teil der Klasse, der von der Außenwelt aus sichtbar ist. Es sollte der einzige sein, der einen Vertrag zu implementieren hat (und der getestet werden könnte). Ihre private / geschützte Methode hat keinen Vertrag zum Erfüllen und / oder Testen.
Siehe eine verwandte Diskussion dort: https://stackoverflow.com/questions/105007/should-i-test-private-methods-or-only-public-ones
Wenn FunctionB öffentlich ist, teste ich nach dem Kommentar einfach beide mit Unit-Test. Sie mögen denken, dass der Test von FunctionA nicht ganz "Einheit" ist (wie er FunctionB nennt), aber ich würde mir keine Sorgen machen: Wenn der FunctionB-Test funktioniert, aber der FunctionA-Test nicht, bedeutet das, dass das Problem nicht in der liegt Subdomain von FunctionB, was für mich als Diskriminator gut genug ist.
Wenn Sie wirklich in der Lage sein möchten, die beiden Tests vollständig zu trennen, würde ich beim Testen von FunctionA eine Art Verspottungstechnik verwenden, um FunctionB zu verspotten (normalerweise wird ein fester bekannter korrekter Wert zurückgegeben). Mir fehlt das Wissen über das C # -Ökosystem, um eine bestimmte Spottbibliothek zu beraten, aber Sie können sich diese Frage ansehen .
quelle
MyClass
und die Methode mit der Funktionalität zu überschreiben, die Sie stubben möchten. Es kann auch eine gute Idee sein, Ihre Frage zu aktualisieren,FunctionB
damit sie öffentlich ist.protected
Methoden sind Teil der öffentlichen Oberfläche einer Klasse, es sei denn, Sie stellen sicher, dass Ihre Klasse nicht in verschiedenen Assemblys implementiert werden kann.Ich stimme der Theorie zu, dass eine zu testende oder zu ersetzende Funktion wichtig genug ist, um kein privates Implementierungsdetail der zu testenden Klasse, sondern ein öffentliches Implementierungsdetail einer anderen Klasse zu sein.
Also, wenn ich in einem Szenario bin, in dem ich habe
Dann werde ich umgestalten.
Jetzt habe ich ein Szenario, in dem D () unabhängig testbar und vollständig ersetzbar ist.
Aus organisatorischen Gründen befindet sich mein Mitarbeiter möglicherweise nicht auf derselben Namespace-Ebene.
A
Befindet sich mein Mitarbeiter beispielsweise in FooCorp.BLL, ist er möglicherweise eine weitere Ebene tiefer als in FooCorp.BLL.Collaborators (oder wie auch immer der Name lautet). Mein Mitarbeiter ist möglicherweise nur über deninternal
Zugriffsmodifikator in der Assembly sichtbar , den ich dann auch über dasInternalsVisibleTo
Assemblyattribut für meine Unit-Test-Projekte verfügbar mache. Der Vorteil ist, dass Sie Ihre API für Anrufer weiterhin sauber halten und gleichzeitig überprüfbaren Code erstellen können.quelle
Ergänzend zu dem, was Martin betont,
Wenn Ihre Methode privat / geschützt ist, testen Sie sie nicht. Es ist innerhalb der Klasse und sollte nicht außerhalb der Klasse zugegriffen werden.
In beiden von Ihnen erwähnten Ansätzen habe ich folgende Bedenken:
Methode 1 - Dies ändert tatsächlich das Verhalten der zu testenden Klasse im Test.
Methode 2 - Hiermit wird der Produktionscode nicht getestet, sondern eine andere Implementierung.
In dem angegebenen Problem sehe ich, dass die einzige Logik von A darin besteht, zu sehen, ob die Ausgabe von FunctionB gerade ist. Obwohl es nur zur Veranschaulichung dient, gibt FunctionB einen Zufallswert an, der schwer zu testen ist.
Ich erwarte ein realistisches Szenario, in dem wir MyClass so einrichten können, dass wir wissen, welche FunctionB zurückgeben würde. Wenn unser erwartetes Ergebnis bekannt ist, können wir FunctionA aufrufen und das tatsächliche Ergebnis bestätigen.
quelle
protected
ist fast das gleiche wiepublic
. Nurprivate
undinternal
sind Implementierungsdetails.internal protected
erstellen, einen privaten Reflection-Helfer verwenden oder eine abgeleitete Klasse in Ihrem Testprojekt erstellen.Ich persönlich verwende Method1, dh, ich verwandle alle Methoden in Actions oder Funcs, da dies die Code-Testbarkeit für mich enorm verbessert hat. Wie bei jeder Lösung gibt es Vor- und Nachteile bei diesem Ansatz:
Vorteile
Nachteile
Zusammenfassend lässt sich sagen, dass die Verwendung von Funktionen und Aktionen für den Komponententest großartig ist, wenn Sie wissen, dass Ihre Klassen niemals außer Kraft gesetzt werden.
Außerdem erstelle ich normalerweise keine Eigenschaften für Funcs, sondern binde sie direkt als solche ein
Hoffe das hilft!
quelle
Mit Mock ist es möglich. Nuget: https://www.nuget.org/packages/moq/
Und glauben Sie mir, es ist ziemlich einfach und macht Sinn.
Mock braucht eine virtuelle Methode zum Überschreiben.
quelle