WPF-Komponentenressourcen während des automatisierten Tests

8

Ich habe einen Punkt erreicht, an dem ich einen automatisierten Test schreiben möchte, um den Inhalt einer WPF-Ansicht zu überprüfen, die an ein Ansichtsmodell in einem bestimmten Zustand gebunden ist.

Im Konzept ist es ziemlich einfach. Erstellen Sie ein Ansichtsmodell, legen Sie seinen Status fest, erstellen Sie die entsprechende Ansicht, fügen Sie die Ansicht zu einem Fenster hinzu, legen Sie den Datenkontext der Ansicht fest, zeigen Sie das Fenster an, erstellen Sie einen Screenshot und vergleichen Sie sie mit einem zuvor aufgenommenen Screenshot. Diese Art von Test ist nützlich, um unbeabsichtigte Änderungen zu erkennen und zu überprüfen, dass alle Ansichten tatsächlich ohne Fehler erstellt werden können.

Das Erstellen einer Instanz meiner Ansicht erweist sich jedoch als problematisch. Es erfordert eine Reihe von Ressourcen, die nicht in der XAML-Definition selbst enthalten sind. Diese Ressourcen sind im Ressourcenwörterbuch der Anwendungsebene in der tatsächlichen Anwendung enthalten. Wenn die Ansicht in der realen Anwendung erstellt wird, sind diese Ressourcen daher bereits verfügbar.

Wenn ich eine Instanz dieser Ansicht in meinem Test erstelle, wird eine XamlParseException ausgelöst, die (verständlicherweise) verschiedene Ressourcen nicht finden kann.

Ich möchte nicht einfach der XAML-Definition der View selbst entsprechende Ressourcenwörterbücher hinzufügen, da dies den Aufwand (Computeraufwand) erhöhen würde, der zum Erstellen eines dieser View-Objekte erforderlich ist, sowie zum Erhöhen der Speichermenge für jede Instanz erforderlich. Mein Verständnis ist, dass dies ein Ergebnis von ResourceDictionary ist nicht auf diese Weise geteilt werden.

Ich habe es versucht:

  • Erstellen einer Instanz von App.xaml innerhalb des Tests (wodurch die Application.Current-Eigenschaft festgelegt wird).
  • Festlegen der Application.Current.Resources-Eigenschaft für die Resources-Eigenschaft einer Instanz von App.xaml.
  • Festlegen der Resources-Eigenschaft der Seite direkt.
  • Einstellung der Ressourcen-Eigenschaft des Fensters direkt (das Fenster, zu dem die Seite für den Test hinzugefügt wird).

Im Wesentlichen muss ich wissen, ob es eine Möglichkeit gibt, eine Situation zu konstruieren, in der ich eine Gruppe von Anwendungsressourcen für eigenständige Instanzen von WPF-Komponenten konfigurieren kann, die in einem automatisierten Test verwendet werden.

Sie können das Problem reproduzieren, indem Sie die folgende Struktur mit allen außer der View_Test.cs-Datei in einem Projekt und der View_Test.cs-Datei, die in einem Testprojekt lebt, erstellen. Führen Sie die App und es funktioniert. Führen Sie den Test und es schlägt fehl.

App.xaml

%Vor%

Styles.xaml

%Vor%

MainWindow.xaml

%Vor%

View.xaml

%Vor%

View_Test.cs

%Vor%

Aktualisierung:

Ich hatte etwas Glück beim Erstellen eines zusätzlichen Konstruktors für die fragliche Ansicht, der ein ResourceDictionary verwendet, um die Ansicht mit einem Kontext für die Initialisierung zu versehen. Diese Konstruktorüberladung wird nur für Tests verwendet, in der realen Anwendung ist der Ressourcenkontext bereits aus den Anwendungsressourcen verfügbar.

%Vor%

Dies löst das spezifische Beispiel, das ich oben gepostet habe, auf eine Weise, die nicht von der Initialisierung nicht verwandter Objekte abhängig ist, nur um die View zum Laufen zu bringen (was angesichts der guten Abhängigkeitsinjektionspraktiken misslingt).

Aber es brachte einige zusätzliche Probleme ans Licht, als ich versuchte, es in meinem eigentlichen Projekt zu implementieren. Mein Ressourcenkontext auf der Anwendungsebene ist eigentlich die Zusammenführung von 4 verschiedenen Ressourcenwörterbüchern, wobei letztere von den früheren abhängig sind (da sie Ressourcen referenzieren, die in einem früheren Eintrag angegeben wurden).

AppResources.xaml

%Vor%

Wenn ich ein ResourceDictionary aus dieser Datei in meinem Testprojekt erstelle und dieses ResourceDictionary während der Konstruktion in meine Ansicht einfüge, wird eine XamlParseException ausgelöst, die sich auf eine StaticResource bezieht, die nicht gefunden wird (die Ressource, die nicht gefunden werden kann, lebt in GlobalBrush) Eintrag in GlobalColour).

Ich werde aktualisieren, während ich weiter erforsche.

Aktualisierung:

Ich hatte absolut kein Glück, das AppResources ResourceDictionary oben manuell zu erstellen und zu verwenden. Ich konnte die Interdependenzen zwischen den Wörterbüchern in den MergedDictionaries nicht erreichen. Ich konnte die ResourceDictionary-Instanz nicht einmal manuell reduzieren, da beim Versuch, auf eine Ressource in einem Wörterbuch zuzugreifen, das von einer Ressource in einem parallelen Wörterbuch abhängig war, eine XamlParseException ausgelöst wurde.

Daher war die Idee, ein ResourceDictionary über einen Konstruktor in die Ansicht einzufügen, für die Verwendung in meiner Lösung nicht praktikabel (obwohl es funktioniert, wenn die App-Ressourcen eine flache ResourceDiction sind).

Am Ende dieser Reise bin ich zu dem Schluss gekommen, dass die einzige Möglichkeit, eine View zu instanziieren, in der das xaml nicht direkt Verweise auf die Ressourcen enthält (ohne die gesamte App instanziieren zu müssen), Verweise auf das entsprechende enthält ResourceDictionary wo immer eine Ressource verwendet wird, direkt in der XAML.Sie müssen dann die Leistungsprobleme zur Laufzeit verwalten (weil Sie Hunderte von doppelten ResourceDictionaries instanziieren), indem Sie ein SharedResourceDictionary verwenden (es gibt eine Anzahl von Implementierungen dieses Konzepts, die im Internet verfügbar sind).

    
Todd Bowles 27.12.2013, 08:03
quelle

3 Antworten

10

Das ist nicht wirklich so schwer, dass Sie nur verwenden müssen Application.LoadComponent , um Instanzen von allem zu erstellen, damit die richtigen Ressourcen zur richtigen Zeit verfügbar sind.

Der Schlüssel ist, alles über seinen XAML zu laden, anstatt Instanzen der Klassen zu erstellen, da die Klasse nur die Hälfte der Informationen enthält.

%Vor%

Bearbeiten: Einfachere Version

Ich habe mir gerade einen disassemblierten Code angeschaut, um zu sehen, wie es intern gemacht wird, und dies kann vereinfacht werden, um die XAML-Referenzen nicht zu benötigen. Die wichtigsten Dinge, um die Dinge in Gang zu bringen, sind das Setzen von Application.ResourceAssembly und das Erstellen von App und das Aufrufen von InitializeComponent . Das Fenster ist nicht notwendig, Sie könnten einfach ein neues Fenster erstellen, um die Ansicht zu halten.

%Vor%     
David Ewen 30.12.2013, 06:27
quelle
2

Was Sie hier wollen, ist ein UI-Test. Microsofts Coded UI Framework wäre Ihre beste Wahl. Das Erstellen eines Screenshots Ihrer Anwendung wird keine zuverlässige Methode sein, um festzustellen, ob sie ordnungsgemäß funktioniert.

Mit einem Test der codierten Benutzeroberfläche können Sie Aktionen für Ihre Benutzeroberfläche aufzeichnen und Assertionen hinzufügen, um zu überprüfen, ob die Benutzeroberfläche ordnungsgemäß funktioniert. Wenn Sie den Test ausführen, wird die Anwendung tatsächlich gestartet und die aufgezeichneten Aktionen werden wiedergegeben, um die Anwendung in den erwarteten Status zu versetzen. Es ist generell ziemlich schmerzlos, damit anzufangen.

Natürlich werden UI-Tests am sparsamsten verwendet, da sie viel langsamer sind. Ein vernünftiger Komponententest sollte einige Millisekunden dauern, aber so ziemlich jeder UI-Test wird mehrere Größenordnungen länger dauern.

    
Daniel Mann 30.12.2013 03:32
quelle
0

Dem System ist es egal, wo es die benötigten Ressourcen findet. Sie müssen irgendwo im visuellen Baum gefunden werden. Deshalb ist es in Ordnung, die Stile zu dem Fenster anstatt der Anwendung hinzuzufügen.

Wenn ich das bedenke, würde ich einfach die Datei style.xaml mit dem XAMLReader laden, was zu einem ResourceDictionary-Objekt führt. Weisen Sie diese Ressource dann Ihrem Window-Objekt zu oder fügen Sie sie hinzu.

    
gomi42 31.12.2013 15:12
quelle

Tags und Links