Um die hervorragende Antwort von emddudley zu ergänzen, besteht der größte Vorteil, den Sie durch das Verspotten des Dienstes erzielen können, darin, zu testen, was passieren sollte, wenn der Dienst nicht richtig funktioniert. Der Testpseudocode könnte ungefähr so aussehen:
public int AgeMinimumValue_LogsServiceError_Test()
{
ClassUnderTest uut = new ClassUnderTest();
MockService service = new MockService();
service.Throws(new TimeoutException());
MockLogger logger = new MockLogger();
try {
int age = uut.getAge(service, logger);
Assert.Fail("Exception was not raised by the class under test");
}
catch (TimeoutException) {
Assert(logger.LogError().WasCalled());
}
}
Und jetzt wurde Ihre Implementierung mit dieser neuen Anforderung bearbeitet
public int getAge(Service s, Logger l)
{
try {
int age = s.execute(empId);
return age;
}
catch(Exception ex) {
l.LogError(ex);
throw;
}
}
In anderen Szenarien ist es wahrscheinlicher, dass Sie auf kompliziertere Antworten reagieren müssen. Wenn der Dienst die Kreditkartenverarbeitung bereitstellt, müssen Sie auf Erfolg, Nicht verfügbarer Dienst, Abgelaufene Kreditkarte, ungültige Nummer usw. reagieren. Durch Verspotten des Dienstes können Sie sicherstellen, dass Sie auf diese Szenarien entsprechend Ihrer Situation reagieren. In diesem Fall müssen Sie die Eingabe / Ausgabe des Dienstes verspotten, und das Feedback, das Sie erhalten, wenn Sie wissen, dass der konsumierende Code für alle bekannten Ausgaben funktioniert, ist in der Tat sinnvoll und wertvoll.
EDIT: Mir ist gerade aufgefallen, dass Sie verspotten möchten, ohne die vorhandene Methode zu ändern. Zu diesem locate("ageservice");
Zweck müsste die Methode geändert werden, um Scheinobjekte in Tests zu unterstützen und den tatsächlichen Dienst zu lokalisieren, sobald er bereit ist. Dies ist eine Variation des Service Locator-Musters, das die Logik zum Abrufen der Implementierung des von Ihnen verwendeten Dienstes abstrahiert. Eine schnelle Version davon könnte folgendermaßen aussehen:
public Service locate(string serviceToLocate) {
if(testEnvironment) // Test environment should be set externally
return MockService(serviceToLocate);
else
return Service(serviceToLocate);
}
Meine Empfehlung wäre jedoch, die Dienstabhängigkeiten in die Konstruktoren zu verschieben:
public int AgeMinimumValue_LogsServiceError_Test()
{
MockService service = new MockService();
service.Throws(new TimeoutException());
MockLogger logger = new MockLogger();
ClassUnderTest uut = new ClassUnderTest(service, logger);
try {
int age = uut.getAge();
Assert.Fail("Exception was not raised by the class under test");
}
catch (TimeoutException) {
Assert(logger.LogError().WasCalled());
}
}
Jetzt ist die getAge-Methode nicht mehr dafür verantwortlich, den Dienst nachzuschlagen, da er vollständig aus der Klasse abstrahiert wurde und eine Implementierung wie diese übrig bleibt:
public int getAge()
{
try {
// _service is a private field set by the constructor
int age = _service.execute(empId);
return age;
}
catch(Exception ex) {
// _logger is a private field set by the constructor
_logger.LogError(ex);
throw;
}
}