HTML Test Reports für Selenium Tests mit ExtentReport Framework
Selenium ist als Testautomatisierungstool für Webanwendungen absoluter Standard. Der Standard WebDriver an sich hat keinen integrierten Reporter und das ist auch nicht seine Kernaufgabe. Diese wird vom WebDriver an die Testframeworks (JUnit / NUnit / MSTest / Jasmine usw.) delegiert. Es kann aber trotzdem vorkommen, dass die von den Testframeworks zur Verfügung gestellten Reporter für Auswertung von den Tests nicht ausreichend sind.
Gute Testreports haben allgemein folgende Vorteile:
- Bieten gute Übersicht über den Testlauf auf verschiedenen Ebenen an: von hoch-granaular als Testmanagement Sicht bis feingranular für die Analyse einzelner Testschritte
- Als lesbarer strukturierter Text / HTML abgelegt, enthalten fachliche Schritte
- Enthalten eingebetete Screenshots, was beim Nachvollziehen der Probleme hilft
- Vereinfachen die Fehlersuche
- Können archiviert werden
- Können von Menschen ohne Programmier-Kenntnissen ausgewertet werden
Auf dem Markt existieren viele mehr oder weniger gute Lösungen, die das Reporting für Selenium Tests bereitstellen. Nicht alle erfüllen die davor genannten Anforderungen an gute Testberichte, aber je nach Anwendungsfall und Projekt ist es auch oft gar nicht notwendig.
Bei uns in den Projekten hat sich der Testreporter ExtentReports bewährt. Neben einer kostenpflichtigen Version, gibt es auch eine Community Edition, die für den Einsatz in den meisten Projekten absolut ausreichend ist.
In diesem Beitrag möchte ich aufzeigen, wie ExtentReports in ein .NET Projekt eingebunden und benutzt werden kann.
Selenium Unit Test Projekt aufbauen
Visual Studio Projekt anlegen
Als erstes legen wir uns ein neues Testprojekt in Visual Studio an. Dazu verwenden wir hier das Standard MSTest Testprojekt von .Net Core
Die anschließend geöffnete .cs Datei sollte etwa so aussehen
using Microsoft.VisualStudio.TestTools.UnitTesting; namespace SeleniumTests { [TestClass] public class UnitTest1 { [TestMethod] public void TestMethod1() { } } }
Selenium Webdriver & Nuget Pakete einbinden
Mit „Rechtsklick auf Projekt -> NuGet-Pakete verwalten… “ die NuGet Paketverwaltung öffnen
und nach „Selenium“ suchen.
Die beiden Pakete Selenium.WebDriver und Selenium.Chrome.WebDriver können installiert und NuGet Package Manager vorerst geschlossen werden.
Ersten Selenium Test implementieren
Wir fügen unserem Selenium Test die erste Funktinalität hinzu, um zu prüfen, ob dieser mit den neuen NuGet Paketen einwandfrei funktioniert:
[TestMethod] public void TestMethod1() { IWebDriver driver = new ChromeDriver(Environment.CurrentDirectory); driver.Navigate().GoToUrl("http://www.google.de"); driver.FindElement(By.Name("q")).SendKeys("Hallo Test"); }
Sobald dieser Test aus dem Test-Explorer gestartet wird, sollte unser erster Test erfolgreich durchlaufen:
ExtentReports Reporter nutzen
Reporter über NuGet Paket einbinden
Nun ist es Zeit den Reporter ExtentReports zu unserem Projekt hinzuzufügen. Dazu benötigen wir das entsprechende Nuget Paket.
Also öffnen wir wieder die NuGet Paketverwaltung und suchen nach ExtentReports:
Wichtig:
Da wir hier ein .Net Core Testprojekt haben, ist es wichtig, den ExtentReports.Core zu verwenden. Bei einem klassischen .Net Framework Projekt (z.B. .Net Framework 4.6) kann auch das erste nicht-core fähige ExtenReports Paket verwendet werden (siehe dazu den Beitrag auf Stackoverflow https://stackoverflow.com/questions/53816428/unable-to-generate-extentreport-for-netcore-app-2-1 ).
Nach der Installation kann der Reporter nun verwendet werden.
Reporter Umgebung konfigurieren
Damit der ExtenReports Reporter saubere Reports produziert, müssen wir diesen zuerst konfigurieren.
Dazu füge ich temporär folgenden Code direkt in die Unit Test Methode ein:
//Report konfigurieren AventStack.ExtentReports.ExtentReports _extent = new AventStack.ExtentReports.ExtentReports(); string folder = Path.Combine(Environment.CurrentDirectory, "report" + DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss")); var htmlReporter = new ExtentHtmlReporter(Path.Combine(folder, "SeleniumTestReport.html")); _extent.AddSystemInfo("Environment", "Testumgebung"); _extent.AddSystemInfo("User Name", "Tester"); _extent.AttachReporter(htmlReporter);
Mit diesem Code erstellen wir einen neuen _extent Reporter und weisen diesem einen HTML Reporter zu. Außerdem werden zwei System-Informationen hinzugefügt. Diese bestehen aus einem Key Value Paar und werden anschließend auf unserer Dashboard Seite dargestellt. Als Zielordner wird ein Ordner mit dem Namen „report<aktueller Zeitstempel>“ verwendet.
Testreporter konfigurieren und verwenden
Nachdem der eigentliche (globale) Report konfiguriert wurde, können einzelne TestReporter für jeden Testfall erstellt werden. Der Code dazu sieht auch sehr ähnlich aus:
//TestReporter konfigurieren ExtentTest _testReporter = _extent.CreateTest("TestMethod1 Erster Selenium Test"); _testReporter.Info("Starte Chrome Browser und öffne URL www.google.de");
Wie in dem Code zu sehen ist, wird zuerst für den Test ein neuer ExtentTest Reporter erstellt und anschließend sofort verwendet. Wir haben hier die Info Methode verwendet, aber wie in den meisten anderen Reportern stehen natürlich weitere Methoden für Error / Warning / Debug Protokollierung zur Verfügung.
Report schreiben
Da ExtentReports mit einer InMemory Datenbank arbeitet, muss am Ende noch die „Schreibeanweisung“ erfolgen, damit die erstellten Log Informationen in eine physikalische Report Datei weggesichert werden. Dazu wird die Methode _extent.Flush() aufgerufen.
Nun sollte der Gesamt-Testfall etwa so aussehen:
[TestMethod] public void TestMethod1() { //Report konfigurieren AventStack.ExtentReports.ExtentReports _extent = new AventStack.ExtentReports.ExtentReports(); string folder = Path.Combine(Environment.CurrentDirectory, "report" + DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss")); var htmlReporter = new ExtentHtmlReporter(Path.Combine(folder, "SeleniumTestReport.html")); _extent.AddSystemInfo("Environment", "Testumgebung"); _extent.AddSystemInfo("User Name", "Tester"); _extent.AttachReporter(htmlReporter); //Testreporter konfigurieren ExtentTest _testReporter = _extent.CreateTest("TestMethod1 Erster Selenium Test"); _testReporter.Info("Starte Chrome Browser und öffne URL www.google.de"); IWebDriver driver = new ChromeDriver(Environment.CurrentDirectory); driver.Navigate().GoToUrl("http://www.google.de"); driver.FindElement(By.Name("q")).SendKeys("Hallo Test"); _extent.Flush(); }
Wird dieser ausgeführt, sollte in dem Testrun-Verzeichnis (Debug bzw. Release) ein neuer Ordner angelegt worden sein. In meinem Fall report2019-04-22-12-18-37, darin sind zwei Dateien zu finden:
- dashboard.html – enthält eine high-level Übersicht über alle Testfälle des Testlaufs mit Charts und allgemeinen Informationen (wie z.B. von uns erstellten Systeminformationen)
- index.html – enthält die Liste aller Testfälle in dem Report mit den dazugehörigen feingranularen Reporteinträgen
Ausbau vom Reporter auf mehrere Tests
ExtentReports kann natürlich auch für das Reporting von Testläufen mit mehreren Testfällen verwendet werden. Dazu sind folgende Schritte notwendig:
- Auslagern der ExtentReports Instanz in die statische
[ClassInitialize] Methode
- Auslagern der schreibenden Methode Flush in die statische [ClassCleanup] Methode
- Auslagern des ExtentTest Reporters in [TestInitialize] Methode
Hier ist ein Beispiel wie ein Report mit zwei Testfällen erstellt werden kann:
[TestClass] public class UnitTest1 { static AventStack.ExtentReports.ExtentReports _extent; static string folder; public TestContext TestContext { get; set; } private ExtentTest _testReporter; [ClassInitialize] public static void Initialize(TestContext context) { _extent = new AventStack.ExtentReports.ExtentReports(); folder = Path.Combine(Environment.CurrentDirectory, "reports" + DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss")); var htmlReporter = new ExtentHtmlReporter(Path.Combine(folder, "Automation_Report.html")); _extent.AddSystemInfo("Environment", "Testumgebung"); _extent.AddSystemInfo("User Name", "Tester"); _extent.AttachReporter(htmlReporter); } [ClassCleanup] public static void CleanUp() { _extent.Flush(); } [TestInitialize] public void TestInitialize() { _testReporter = _extent.CreateTest(TestContext.TestName); } [TestMethod] public void TestMethod1() { IWebDriver driver = new ChromeDriver(Environment.CurrentDirectory); driver.Navigate().GoToUrl("http://www.google.de"); _testReporter.Log(Status.Info, "TestMethod1 Erster Selenium Test"); driver.FindElement(By.Name("q")).SendKeys("Hallo Test"); _testReporter.Log(Status.Info, "Test ended"); _testReporter.Pass("Alles bestens!"); } [TestMethod] public void TestMethod2() { IWebDriver driver = new ChromeDriver(Environment.CurrentDirectory); driver.Navigate().GoToUrl("http://www.google.de"); _testReporter.Log(Status.Info, "Tippe ein in Google Suchfeld"); driver.FindElement(By.Name("q")).SendKeys("Hallo Welt"); try { //suche nach unbekanntem Element - hier provozieren wir einen Fehler driver.FindElement(By.Name("asdfasdf")).SendKeys("Hallo Welt"); } catch (Exception e) { _testReporter.Fail(e.Message); _testReporter.Debug(e.StackTrace); throw; } } }
Anhängen von Screenshots an den Reporter
ExtentReports kann ebenfalls Screenshots an die Testschritte in Report hinzufügen, um die Fehleranalyse nachvollziehbarer und effizienter zu gestalten.
Dazu muss lediglich die Standard Screenshot Funktionalität von Selenium verwendet werden und anschließend CreateScreenCaptureFromPath() Methode aufgerufen werden.
Hier ist ein Beispiel, wie im Fehlerfall ein Screenshot geschossen werden kann:
using Microsoft.VisualStudio.TestTools.UnitTesting; using OpenQA.Selenium; using OpenQA.Selenium.Chrome; using System; using System.IO; using AventStack.ExtentReports; using AventStack.ExtentReports.Reporter; namespace SeleniumUnitTest { [TestClass] public class UnitTest1 { static AventStack.ExtentReports.ExtentReports _extent; static string folder; public TestContext TestContext { get; set; } private ExtentTest _testReporter; [ClassInitialize] public static void Initialize(TestContext context) { _extent = new AventStack.ExtentReports.ExtentReports(); folder = Path.Combine(Environment.CurrentDirectory, "reports" + DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss")); var htmlReporter = new ExtentHtmlReporter(Path.Combine(folder, "Automation_Report.html")); _extent.AddSystemInfo("Environment", "Testumgebung"); _extent.AddSystemInfo("User Name", "Tester"); _extent.AttachReporter(htmlReporter); } [ClassCleanup] public static void CleanUp() { _extent.Flush(); } [TestInitialize] public void TestInitialize() { _testReporter = _extent.CreateTest(TestContext.TestName); } [TestMethod] public void TestMethod1() { IWebDriver driver = new ChromeDriver(Environment.CurrentDirectory); driver.Navigate().GoToUrl("http://www.google.de"); _testReporter.Log(Status.Info, "TestMethod1 Erster Selenium Test"); driver.FindElement(By.Name("q")).SendKeys("Hallo Test"); _testReporter.Log(Status.Info, "Test ended"); _testReporter.Pass("Alles bestens!"); } [TestMethod] public void TestMethod2() { IWebDriver driver = new ChromeDriver(Environment.CurrentDirectory); driver.Navigate().GoToUrl("http://www.google.de"); _testReporter.Log(Status.Info, "Tippe ein in Google Suchfeld"); driver.FindElement(By.Name("q")).SendKeys("Hallo Welt"); try { //suche nach unbekanntem Element driver.FindElement(By.Name("asdfasdf")).SendKeys("Hallo Welt"); } catch (Exception e) { string path = CaptureScreenshot(driver); _testReporter.Fail(e.Message, MediaEntityBuilder.CreateScreenCaptureFromPath(path).Build()); _testReporter.Log(Status.Debug, e.StackTrace); throw; } } public static string CaptureScreenshot(IWebDriver driver) { string localpath = ""; try { string name = "screenshot_" + DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss-fff"); ITakesScreenshot ts = (ITakesScreenshot)driver; Screenshot screenshot = ts.GetScreenshot(); localpath = Path.Combine(folder, name + ".png"); screenshot.SaveAsFile(localpath, ScreenshotImageFormat.Png); } catch (Exception e) { throw (e); } return localpath; } } }
So sollte nun der TestReport aussehen:
Zusammenfassung
Nach nur wenigen Handgriffen konnten wir auf Schnelle eine komfortable Reporting Lösung zu unserem Selenium Test-Projekt hinzufügen. Mit dieser Lösung wird bei jedem Testlauf ein interaktiver Html-Report generiert, der uns eine übersichtliche Dashboard Seite anbietet. Von Dashboard aus kann man direkt zu einzelnen Testfällen navigieren und dort sowohl die Testdetails mit zugehörigen Schritten als auch eingebettete Screenshots kontextgenau sich anschauen.
Probiert es am besten gleich in Euren Projekten aus. Ich freue mich auf das Feedback, wie Ihr es einsetzt und ob diese Lösung für Eure Projekte hinreichend ist…
Begeisterter Entwickler, Trainer und Berater zu allem Themen rund um agile Softwareentwicklung, Softwarequalität und Testautomatisierung.
Gründer und Geschäftsführer der Firma SimplyTest GmbH
Ich kann Allure empfehlen. Lässt sich sehr einfach einbinden und ist sehr übersichtlich