2016-07-28 19 views
1

Okay, ich weiß, das klingt wie eine seltsame Anfrage, aber hier ist mein Problem. Ich möchte einige Integrationstests für meinen WCF-Dienst schreiben. Ich habe ein paar Schlüsselpfade, die ich sicherstellen will, dass sie sich richtig verhalten. Ein Test besteht darin, sicherzustellen, dass die richtigen Ausnahmen an Schlüsselstellen ausgegeben werden und dass sie die Pipeline korrekt weiterleiten, ohne dass sie an der falschen Stelle abgefangen werden.Entfernen Sie registrierte Dekorateur in Simple Injector

Um dies zu tun, überschreibe ich eine bestehende Registrierung mit einem Mock-Objekt, das die Ausnahme, die ich testen möchte, an dem Ort, an dem ich es geworfen werden will. Dieser Teil funktioniert gut.

Als nächstes möchte ich meine Befehlshandler auflösen (das System im Test), rufen Sie die Handle-Methode auf, und bestätigen Sie, dass die richtige Ausnahme passiert.

Das Problem ist, dass, wenn ich meinen Befehlshandler auflösen, ich tatsächlich eine loooong Kette von Dekoratoren mit meinem Befehlshandler ganz unten zurückbekomme. Ganz oben in dieser Kette befindet sich ein Decorator, der mein globaler Ausnahme-Handler ist. Es ist diese Ausnahme, die den Dekorator oben behandelt, dass ich die Registrierung aufheben muss, weil es verhindert, dass ich feststellen kann, dass die Ausnahme ausgelöst wurde. Mein Container-Bootstrapper ist ziemlich komplex, so dass ich absolut keine Lust habe, eine Kopie davon in meinem Testprojekt ohne diesen einen Dekorateur neu zu erstellen.

Wenn es nur eine Standardregistrierung wäre, könnte ich einfach die Registrierung mit einem falschen Ausnahme-Handler überschreiben, der die Ausnahme erneut ausliest. Soweit ich das beurteilen kann, scheint es jedoch nicht möglich zu sein, die Registrierung eines Dekorators zu überschreiben. Ich würde es trotzdem vorziehen, diesen Weg nicht zu gehen. Es macht den Test mit einem zusätzlichen Schein noch komplizierter. Es wäre viel besser, wenn ich den Dekorateur einfach abmelden könnte.

Wenn es nicht möglich ist, einen Dekorateur abzumelden, was wäre meine nächste beste Lösung? Füge Optionsflags zu meinem Bootstrapper hinzu, um bestimmte Registrierungen zu aktivieren/deaktivieren?

Danke.

Antwort

3

Es ist unmöglich, eine Registrierung in Simple Injector zu entfernen. Sie können replace an existing registration, aber diese Methode funktioniert nicht im Umgang mit Dekorateuren. Decorators werden intern in Simple Injector hinzugefügt, indem dem Ereignis ExpressionBuilt ein Delegat hinzugefügt wird. Da der registrierte Delegierte nirgendwo gespeichert ist, ist es derzeit technisch unmöglich, eine Dekorationsregistrierung "abzutragen".

Der Weg um dies ist einfach nicht registrieren, dass Dekorateur überhaupt. Das hört sich vielleicht albern an, aber ich benutze es die ganze Zeit, auch mit anderen Containern.

Sie können zum Beispiel den gemeinsamen Teil Ihrer Registrierungen auf eine separate Methode extrahieren, nennen wir sie BuildUp. Bei dieser Methode fehlen die Registrierungen, die sich von den verschiedenen Anwendungen, die sie verwenden, unterscheiden. In Ihrem Fall haben Sie mindestens 2 "Anwendungen"; die reale Anwendung und das Integrationstestprojekt. Beide Projekte können die BuildUp anrufen und zusätzliche Registrierungen vor oder nach dem Aufruf BuildUp hinzufügen. Zum Beispiel:

var container = new Container(); 
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle(); 

container.RegisterDecorator(typeof(ICommandHandler<>), 
    typeof(InnerMostCommandHandlerDecorator<>)); 

CompositionRoot.BuildUp(container); 

container.RegisterDecorator(typeof(ICommandHandler<>), 
    typeof(OuterMostCommandHandlerDecorator<>)); 

Diese Methode scheint sehr gut in Ihrem Fall zu arbeiten, da Sie eine ‚äußere meisten‘ Dekorateur hinzufügen möchten. Außerdem lässt es die BuildUp "Löcher" in Ihrer Registrierung lassen es oft sehr einfach zu sehen, wenn einige Anwendung vergisst, die Lücken zu füllen, da Sie Simple Injector schnell ausfallen lassen können, indem Sie container.Verify() aufrufen.

Eine andere übliche Möglichkeit, ein Konfigurationsobjekt an die BuildUp Methode zu übergeben. Dieses Konfigurationsobjekt kann die notwendigen Informationen enthalten, um den richtigen Satz von Registrierungen vorzunehmen, wenn der Anrufer dies benötigt. Zum Beispiel kann ein solches Konfigurationsobjekt eine einfache Boolesche Flag hat:

public static void Build(Container container, ApplicationConfig config) { 

    // Lot's of registrations 

    if (config.AddGlobalExceptionHandler) { 
     container.RegisterDecorator(typeof(ICommandHandler<>), 
      typeof(GlobalExceptionHandlerCommandHandlerDecorator<>)); 
    } 
} 

Vorbei an einem Konfigurationsobjekt auf die BuildUp Methode ist auch eine gute Möglichkeit, Ihre BuildUp Methode aus dem Konfigurationssystem zu entkoppeln. Auf diese Weise können Sie während des Integrationstests einfacher BuildUp aufrufen, ohne eine Kopie der vollständigen Konfigurationsdatei im Testprojekt zu benötigen.

Anstatt eine Flag-Eigenschaft zu verwenden, können Sie auch die vollständige Liste der Decorators innerhalb des Konfigurationsobjekts haben und die BuildUp-Methode durchlaufen lassen und sie registrieren. Dies ermöglicht es der Anrufer Dekorateure aus der Liste zu entfernen oder hinzufügen, bevor sie registriert sind:

var config = new ApplicationConfig(); 

// Remove decorator 
config.CommandHandlerDecorators.Remove(
    typeof(AuthorizationCommandHandlerDecorator<>)); 

// Add decorator after another decorator 
config.CommandHandlerDecorators.Insert(
    index: 1 + config.CommandHandlerDecorators.IndexOf(
     typeof(TransactionCommandHandlerDecorator<>)), 
    item: typeof(DeadlockRetryCommandHandlerDecorator<>)); 

// Add an outer most decorator 
config.CommandHandlerDecorators.Add(
    typeof(TestPerformanceProfilingCommandHandlerDecorator<>)); 

CompositionRoot.BuildUp(container, config); 


public static void BuildUp(Container container, ApplicationConfig config) { 

    // Lot's of registrations here. 

    config.CommandHandlerDecorators.ForEach(type => 
     container.RegisterDecorator(typeof(ICommandHandler<>), type)); 
} 

Ich habe alle drei Methoden in der Vergangenheit sehr erfolgreich eingesetzt. Welche Option zur Auswahl steht, hängt von Ihren Bedürfnissen ab.

3

Soweit ich weiß, ist es nicht möglich, eine Registrierung zu entfernen.

Mit Unit-Tests würden Sie normalerweise den Container überhaupt nicht verwenden. Da Sie Integrationstests durchführen, ist die Verwendung des Containers in der Tat ein Muss.

Ich kann an 2 Möglichkeiten denken, zu tun, was Sie wollen.

Die erste besteht darin, ein Optionsflag an den Bootstrapper zu übergeben, der zwischen der Produktions- und Testumgebung wechselt.

Die zweite ist das Nachdenken über Ihren Testansatz. Aus Ihrer Frage scheint es, dass ein bestimmter Teil Ihrer ICommandHandler-Kette eine Ausnahme auslösen sollte.

Ich würde denken, das ist ziemlich einfach mit einem normalen Komponententest anstelle eines Integrationstests zu testen. In diesem Fall würden Sie den Container nicht verwenden, sondern die Kette manuell erstellen.

für Sie einen Unittest Command würde so einfach sein wie:

[TestMethod] 
[ExpectedException(typeof(InvalidOperationException))] 
public void CommandHandlerThrowsCorrectException() 
{ 
    var handler = new Decorator1(new Decorator2(new MyHandler())); 

    handler.Handle(new MyCommand); 
} 

Sie andere Integrationstests verwenden können, wenn ein Befehl an die WCF-Dienst Ergebnisse in Gebäuden und Umgang mit der richtigen ICommandHandler Kette übergeben zu überprüfen.

ich normalerweise verwenden Sie die folgende Testaufbau:

  • Verwenden Unit-Tests zur Prüfung aller Fälle, die Sie zur Hand haben, ohne einen Behälter mit =>, die mindestens eine Einheit Test pro Komponente in der Anwendung bedeutet
  • Verwenden Sie Komponententests zum Testen jedes einzelnen Decorators, ohne Verwendung des Containers => so die GenericExceptionCommandHandlerDecorator in Ihrem Fall, behandeln eine Ausnahme
  • Verwenden Sie Komponententests für die Prüfung, ob die Registrierungen an den Container korrekt sind und wenn Dekoratoren angewendet werden in der richtigen Reihenfolge, natürlich mit der Verwendung des Conta iner. Ein Teil dieses Jobs wird bereits vom Container ausgeführt, wenn Sie das integrierte verification of the made registrations verwenden, mit container.Verify()
  • Verwenden Sie so wenig Integrationstests, nur um zu testen, ob die Anwendung funktioniert und wie es sein soll. Da jede Komponente und jeder Dekorator einzeln getestet wird, ist die Notwendigkeit, das Verhalten der Anwendung in Integrationstests zu testen, weitaus geringer. Es wird immer Szenarios geben, in denen Sie die Benutzerinteraktion mit der Anwendung nachahmen wollen, aber es sollte selten sein und meistens durch Komponententests abgedeckt werden.