Software-Entwicklung

Unit Tests mit Sitecore

Sitecore und Unit-testen gehen nicht immer Hand in Hand, aber es gibt gute Möglichkeiten beides erfolgreich zu vereinen. Anhand meiner Erfahrungen aus der Praxis stelle ich verschiedene Varianten vor, wie man Unit-testen in Sitecore-Projekten erfolgreich einsetzen und damit Produktivität und Codequalität steigern kann.

Programming Work Time. Programmer Typing New Lines of HTML Code.

Was ist Sitecore?

Was Sitecore Content Management System ist, beschreibt der Blogartikel von meinem Kollegen Alexander Schmid sehr gut.
Kurz zusammengefasst ist Sitecore eine umfangsreiche Plattform für Entwicklung von Webapplikationen. Sitecore bietet Lösungen für Content-Management, Online-Marketing und E-Commerce und basiert auf C#, ASP.NET.

 

Unit-testen?! Warum?

Wenn man mit der Sitecore Plattform eine Webseite entwickelt, muss man die gleichen Bedenken haben wie bei jeder Sorte von Software-Entwicklung. Dazu gehört auch natürlich Unit-testen. Unit-testen ist ein Produktivitäts-Tool und ist auch grundsätzlicher Teil der Continuous Integration Pipeline. Lohnt es sich wirklich Unit Tests zu schreiben? Mit dieser allgemeinen Frage beschäftigt sich dieser Blogartikel.

 

Sitecore .NET API

Die Sitecore Plattform wird schon seit 2001 entwickelt und Teile der Programmierschnittstelle sind nicht modern, nicht einfach Unit-testbar implementiert worden. Mit den neuen Versionen gibt es Verbesserungen in dieser Hinsicht, aber viele Komponente sind noch nicht abstrahiert, wie z.B. die Sitecore.Data.Items.Item Klasse und die Pipelines.

 

Unit testen! Aber wie?

Allgemeine Konzepte für Unit Tests gelten auch bei Sitecore. Man sollte die Tests mit gleicher Code Qualität programmieren wie sonstiges Programcode, es sollte einfach sein mit dem Unit Test Code zu arbeiten. Man sollte die FIRST Prinzipien berücksichtigen. Nicht vergessen, dass die Unit Tests isoliert sein sollten. Das heißt auch das in Unit Tests keine HTTP Aufrufe, Datenbank- und Filezugriffe, etc. aufgerufen sein sollten. Man sollte die Konzepte für Unit Testen mit Integration bzw. Functional Testing nicht vermischen.

Was für Framework man für die Unit Tests und Mocking benutzt ist aus der Sicht von Sitecore nicht relevant.

Man sollte allerdings eine Unit Test Strategie definieren: Wie schreibt man Unit-testbares Code mit Sitecore? Wie garantiert man die Qualität der Unit Tests?

 

Abstraktion und Interfaces

Eine saubere Lösung Code Unit-testbar zu halten ist Abstrahierung von Abhängigkeiten der getesteten Klasse. Falls Sitecore schon Interfaces für Funktionalitäten zur Verfügung stellt, haben wir nichts zu tun, wir können diese abstrakte Abhängigkeiten mocken. Aber wenn Sitecore Funktionalitäten durch Klassen ohne Interface oder durch statischen Klassen aufrufbar sind, dann können wir diese Klassen meistens nicht direkt in unserem Code benutzen. Solche Sitecore-Klassen können wir selbst abstrahieren.

Eine pragmatische Vorgehensweise für diesen Fall wäre Humble Object Pattern. Das heißt man implementiert eine sehr dünne Wrapper-Schicht, die den nicht testbaren Komponenten aufruft. Diese Klasse wird dann nicht Unit Testet, darum sollte sie wirklich „bescheiden“ und dünn, ohne Logik bleiben.

 

Beispiel für Humble Object um das Sitecore Database Objekt zu abstrahieren.

Abb.: Beispiel für Humble Object um das Sitecore Database Objekt zu abstrahieren.

 

Man sollte aufpassen, dass man bei der Abstrahierung die Klassen und Interfaces nicht zu groß macht und die Single Responsibility Principle einhält.

Allgemein kann man beliebige Code-Architektur und Design Patterns benutzen, abhängig von der Komplexität der Applikation. Z.B. Repository Pattern, Service Layer Pattern oder basierend auf Clean Architecture.

Soweit klingt das schön, aber leider ist das mit Sitecore nicht immer so einfach. Um einige Features von Sitecore zu benutzen, wie das Experience Editor, muss man die Sitecore Klassen direkt referenzieren oder GlassMapper benutzen. Darüber schreibe ich noch später.

 

Dependency Injection

Wenn die Sitecore Funktionalitäten als Interfaces zu Verfügung stehen, müssen sie in unsere Klassen mit Dependency Injection eingebunden werden, um Unittestbarkeit gewährleisten zu können. Mit Sitecore sollte man Constructor Injection benutzen. Z.B. mit der Abhängigkeit an IDatabase Interface so:

Unsere Klassen werden typischer Weise von Sitecore Factory Klassen erstellt und am Anfang in die Controller Objekte injektiert.

Um die Dependency Injections zu managen sollte man die Sitecore IoC Container benutzen, das man einfach Anhand dieser Beschreibung einbinden kann. Es ist empfohlen die Registrierung im Code statt in der Konfiguration zu machen.

Wenn man bei einer Klasse aus irgendwelchem Grund die IoC Container nicht benutzen kann, kann man Poor man’s dependency injection benutzen. Das heißt, dass man selbst die Klassen instanziiert und injectet. In den Unit Tests kann man die gemockte Klassen injecten.

Wenn man IoC Container für Sitecore Experience Accelerator einbinden möchte, hilft diese Beschreibung.

 

Experience Editor und Herausforderungen mit der Abstraktion

Wenn man auf einer ASP.NET MVC Page das Sitecore Experience Editor benutzen möchte, braucht man im View eine direkte Referenz auf Sitecore.Data.Items.Item Objekte.

Ohne eine Referenz auf ein Item Objekt kann man die Sitecore HTML Helper Methode nicht aufrufen, die das extra Code für das Experience Editor generiert.

Entweder gibt man im View das Item und den Feldnamen an:

Wo myItem Typ Item hat.

Oder das Item muss im statischen Sitecore.Context.Item gesetzt sein und dann kann man die Methode so aufrufen:

Das heißt, dass wir mit dieser schwierig Unit-testbaren Klasse in unserem Code arbeiten müssen. In vielen anderen Fällen kann die vorher beschrieben Vorgehensweise mit Abstraktionen folgen, aber an einigen Stellen braucht man diese Sitecore Klassen.

Um das zu überwinden kommt GlassMapper zur Hilfe.

 

GlassMapper.Sc

GlassMapper ist eine ORM (Object-relational mapping) und Abstraktions-Layer für Sitecore. Mit Glass kann man Sitecore Items auf unsere Sitecore-agnostische, POCO Klassen mappen. Es ist Open Source und ist ein populäres Tool für Sitecore Entwickler, das man als Nuget Package einfach installieren kann.

Glass bietet ein Interface für die Sitecore Datenbank, mit dem man Sitecore Items lesen, editieren, erstellen und auf unsere Klassen mappen können, ohne dass wir Sitecore Klassen benutzen. Es bietet auch HTML Helpers die unsere Sitecore Felder korrekt rendern.

Die Lösung für das vorherige Problem um ein Feld mit Experience Editor inline editierbar zu machen ist die GlassMapper HTML Helper Methode zu benutzen. GlassMapper kümmert sich um die Experience Editor Funktionalitäten und wir können unsere eigene POCO Klasse beibehalten.

Ein Beispiel Model Klasse und wie es mit der Hilfe von Glass erstellt wird:

Wenn wir die MyPage Klasse in unserer View als Model haben, können wir es einfach editierbar machen:

GlassMapper Fazit

GlassMapper ist ein sehr hilfreiches Tool für Abstrahierung und als ORM. Wenn man es anfängt zu benutzen wird großes Teil unseres Codes auf Glass basieren, darum muss man gut es gut überlegen ob man diese Abhängigkeit haben möchte. Falls in Glass Bugs auftauchen, Performanzprobleme entstehen, die API sich grob verändert, etc., hat das große Auswirkungen auf unsere Applikation.

 

Unit Testen mit Fake.DB

Eine andere Lösung um die Design Probleme im Sitecore Framework Code zu überwinden und Unit-testen zu können ist die Benutzung von Sitecore FakeDb.

Fake Db ist eine „gefälschte“ In Memory Sitecore Datenbank, die wir in unsere Unit Tests einfach einbinden können und Content für unsere Tests liefern können. Sitecore Fake Db mockt viele Aspekte von Sitecore, wie auch Authentication, Roles, Pipelines, Settings, etc… und alles läuft schnell ohne irgendwelche I/O Operationen.

Ein Beispiel wie man ein Sitecore.Data.Items.Item Objekt erstellen kann:

Diese Lösung hat den Vorteil im Vergleich zu GlassMapper, dass unser Produktiv-Code nicht von einem extra Framework abhängig wird, nur von Sitecore. Nachteil ist, dass unser Code von der Item Klasse abhängig wird und nicht sauber separiert ist. Unsere Model Klasse soll zum Beispiel ein Property mit Typ Item haben.

Fake Db ist in den meisten Fällen eine bessere Lösung als mit Mocking Frameworks, wie TypeMock zu arbeiten und auch besser als selbst die Sitecore Item Objekte zu erstellen.

Interessanterweise wird in der offiziellen „best practice“ Sitecore Implementation FakeDb statt GlassMapper benutzt. Diese Implementation Habitat wird anhand der Helix Architektur implementiert in dem man auch andere hilfreiche Konzepte für Sitecore Entwicklung finden kann.

 

Unit Testen von Legacy Systeme

Legacy Sitecore Implementationen, die nicht Unit-testbar designed wurden muss man meistens überarbeiten.

Wenn man das Refactorn sich sparen muss und trotzdem Unit Tests schreiben möchte, kann man mit Fake Db vieles überwinden, indem man die Voraussetzungen für die Klassen und Tests in der Fake Db erstellt.

Von der Benutzung von Mock Frameworks für Legacy Systeme wie TypeMock würde ich abraten, da es ziemlich aufwendig ist alle Sitecore Abhängigkeiten zu mocken.

Integration Tests, die relative schnell ausführbar sind, können oft einfacher entwickelt werden und können auch eine valide kurzfristige Alternative sein.

 

Fazit

Man kann sehen, dass Unit-testen mit Sitecore kein triviales Thema ist, da das Framework nicht dafür designed wurde. Aber das sollte niemanden von Unit-testen abschrecken.

Wir können nur hoffen, dass Sitecore selbst nicht nur an neue Features konzentrieren wird, sondern auch weiterarbeitet das grundlegende Programmierungs-Framework moderner zu gestalten.

Eine Zusammenfassung von den beschrieben Unit Test Strategien:

 

Unit Test Strategie Vorteil Nachteil
Abstrahierung selbst implementieren Keine extra Frameworks notwendig. Nicht möglich für bestimmte Fälle, z.B. wenn man Felder im Experience Editor editierbar haben möchte.
GlassMapper Elegante Lösung für Unit-testbares Code. Unser ganzes Code-Base wird von diesem Framework abhängig.
FakeDb
  • Keine extra Frameworks nötig im Produktiv-Code
  • Unit Testen von Legacy Code
  • Unsere Klassen sind nicht völlig abstrahiert von Sitecore
  • Extra Framework für Unit Test Code

Passende Artikel

Antwort schreiben

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*