In einer Reihe von Artikeln in diesem Titel werden Implementierungen zum Testen und Produzieren unter Verwendung von Entwurfsmustern betrachtet. Im vorherigen Artikel (http://qiita.com/progrommer/items/a04a74d8b9f43eaef4b9) habe ich das Proxy-Muster und das Monostate-Muster vorgestellt, die grundlegende Konzepte und benutzerfreundliche Formen aufweisen. Dieses Mal werde ich beschreiben, wie die zu testende Klasse entworfen wird. Nach dem letzten Mal lautet das Thema Dependency Injection (DI).
Das Beispielprogramm ist eine Mischung aus Python, C # und C ++, aber wir planen, es zu gegebener Zeit zu ergänzen. Bitte verzeih mir.
Einige schwer zu testende Klassen- (oder Funktions-) Implementierungen werden an derselben Stelle erstellt und verwendet.
// Python
class Some:
def get(self):
return 42
def func():
some = Some()
value = some.get()
if value < 10:
pass
// C++
struct Some {
int get() const {return 42;}
};
void func() {
if (Some().get() < 10) return;
}
In diesem Beispiel wird eine Instanz der Klasse Some im laufenden Betrieb erstellt und die Funktion aufgerufen. Dies macht es unmöglich, in Some einzugreifen, selbst wenn Sie versuchen, die Funktionsfunktion zu testen.
Es ist auch wichtig, den Fall zu beachten, in dem ein Element vom Konstruktor generiert und dann von einer beliebigen Funktion verwendet wird. Im folgenden Beispiel kann der Testcode der Zielklasse m_some nach dem Generieren von Some ersetzen, und es besteht Raum für Eingriffe. Auf den ersten Blick sieht das gut aus. Wenn Sie jedoch Some aus einer anderen Klasse oder Funktion aufrufen, können Sie diese Klasse oder Funktion nicht testen.
// C++
Target::Target()
: m_some(new Some("abc"))
{
}
Target::exec() {
m_some->get();
}
//Eine weitere zu testende Funktion
void func() {
Target target;
target.exec(); //Original Einige werden in Target generiert und verwendet
}
Wenn die oben erwähnte feste Erzeugung und Verwendung durchgeführt wird, wird der Einheitentest schwierig. Sobald auch nur eine solche Klasse erstellt wurde, ist es schwierig, sie zu entwirren. Aus diesem Grund ist es wünschenswert, die Klasse zu entwerfen, indem Erzeugung und Verwendung von der frühesten Stufe getrennt werden. Es gibt verschiedene Möglichkeiten, dies zu tun, aber hier sind einige Beispiele, die tatsächlich einfach zu verwenden sind.
Das Entwurfsmuster enthält mehrere "Generierungs" -Muster, aber verwenden wir eines davon, das abstrakte Fabrikmuster.
Im folgenden Beispiel verwendet die Benutzer-Client-Klasse einen Generator namens Creator. Sie erstellen die benötigten Logger- und File-Instanzen aus der Creator-Instanz, die Sie erhalten haben. Die Substanz von Creator ist TestCreator, das in main () erstellt wurde. Es wurde durch eine Testklasse ersetzt, die an die Konsole ausgegeben und eine Dummy-Datei generiert wird.
Klassenname in der Abbildung | Beispielklassenname |
---|---|
ICreator | Creator |
TargetCreator | Keiner |
Target1 | Logger |
Target2 | File |
Target1Object | Keiner |
Target2Object | Keiner |
ClientTest | Hauptfunktion |
DummyCreator | TestCreator |
Dummy1Object | ConsoleLogger |
Dummy2Object | DummyFile |
# Python
class Logger:
def write(self, msg: str) -> None:
pass
class ConsoleLogger(Logger):
def write(self, msg: str) -> None:
print(msg)
class File:
pass
class DummyFile(File):
pass
class Creator:
def create_logger(self) -> Logger:
return None
def create_file(self) -> File:
return None
class TestCreator(Creator):
def create_logger(self) -> Logger:
return ConsoleLogger()
def create_file(self) -> File:
return DummyFile()
class Client:
def __init__(self, creator: Creator):
self._logger = creator.create_logger()
self._file = creator.create_file()
def exec(self) -> None:
#Etwas zu verarbeiten
self._logger.write('Erfolgreiche Fertigstellung')
def main():
creator = TestCreator()
client = Client(creator)
client.exec()
Ein weiteres Beispiel für ein "Generierungs" -Muster ist Singleton. Singleton kann nicht eingreifen, da die Generation innerhalb ihrer Klasse geschlossen ist. Der Zweck besteht darin, nicht eingreifen zu können, aber es ist schwierig, ihn zum Testen zu verwenden.
Hier lockern wir die Struktur von Singleton ein wenig und bereiten einen Mund für die Intervention vor. Die Instanz ist geschützt und alle Methoden verwenden entweder NVI oder nur virtuell.
Das folgende Beispiel zeigt das Ersetzen der Singleton-Klasse und der Testklasse in C #. Der zweite Aufruf von Print (), der zu Dummy wechselt, gibt "Dummy" für dieselbe Methode aus. Zum Zeitpunkt des Unit-Tests sollte es im Vorbereitungsprozess eingestellt werden, damit Dummy von Anfang an aufgerufen wird.
// C#
public class Some {
protected static Some instance;
static Some() {
instance = new Some();
}
static public Some getInstance() {
return instance;
}
virtual public void Print() {
Console.WriteLine("Some");
}
}
internal class SomeDummy : Some {
internal static void UseDummy() {
instance = new SomeDummy();
}
public override void Print() {
Console.WriteLine("Dummy");
}
}
public class Client {
static void Main(string[] args) {
{
var target = Some.getInstance();
target.Print(); // "Some"Und Ausgabe
}
SomeDummy.UseDummy(); //Ersetzen Sie Singleton
{
var target = Some.getInstance();
target.Print(); // "Dummy"Und Ausgabe
}
}
}
Abstrakte Fabrik kann ärgerlich sein, weil sie die Fabrik bei der Klassengenerierung passieren muss. Anwendungen wie die Bereitstellung einer Fabrik mit Singleton oder Monostate können in Betracht gezogen werden. Geben Sie zum Zeitpunkt des Komponententests natürlich eine Fabrik zurück, die eine Dummy-Klasse zum Testen generiert. Verwenden Sie dies, um das erste Beispiel zu ersetzen:
// Python
class FactoryServer:
some_creator = SomeCreator()
def func():
some = FactoryServer.some_creator()
value = some.get()
if value < 10:
pass
Ein Beispiel für die Trennung von Erzeugung und Verwendung wird gezeigt.
Im Beispiel der Abstract-Factory haben wir eine Struktur gezeigt, in der die tatsächliche Klasse und die Klasse für Unit-Tests frei ersetzt werden können, indem das Objekt sowohl für die Erstellung als auch für die Verwendung abstrahiert wird. Im Singleton-Beispiel wurde das Produkt eingegriffen, um eine Testinstanz zu verwenden. Als Anwendung haben wir durch die Kombination von Singleton / Monostate und Abstract Factory eine Konfiguration angegeben, in der Testinterventionen gleichzeitig eingerichtet werden können, während die Einfachheit der Implementierung erhalten bleibt.
Recommended Posts