Logging Selenium WebDriver
(2021-03-29)Selenium is a versatile tool that is widely used in spite of its age. It's a good tool for driving web browsers, and it is highly googleable when you encounter problems. One of the limitations of Selenium is that it is horrible at logging what it does. To mithigate this the following wrapper class has been devloped.
Example usage:
[TestMethod]
public void TestMethod1()
{
var driver = new ChromeDriver();
driver.Navigate().GoToUrl("http://damberg.one");
driver.FindElement(By.XPath("//a")).Click();
Assert.IsTrue(driver.Title.ToLower().Contains("diverse"));
driver.Close();
driver.SwitchTo().Window(driver.WindowHandles[0]);
Assert.IsTrue(driver.Title == "CV");
driver.Close();
}
Renders no output at all. But if you just wrap the ChromeDriver instance with a LoggingWebDriver, like this:
[TestMethod]
public void TestMethod1()
{
var driver = new LoggingWebDriver(new ChromeDriver());
driver.Navigate().GoToUrl("http://damberg.one");
driver.FindElement(By.XPath("//a")).Click();
Assert.IsTrue(driver.Title.ToLower().Contains("diverse"));
driver.Close();
driver.SwitchTo().Window(driver.WindowHandles[0]);
Assert.IsTrue(driver.Title == "CV");
driver.Close();
}
...you'll get this output instead:
12:32:40 Executing : Navigated to URL 'http://damberg.one'.
12:32:40 Executing : Clicking element aElement.
12:32:40 Info : Retrieving title 'Diverse@damberg.one'.
12:32:40 Executing : Switching to window with name 'CDwindow-11E45A9B9F0F4C93573C04D48AFF5598'.
12:32:41 Info : Retrieving title 'CV'.
Maybe you want to debug your code so you change logging level (with: var driver = new LoggingWebDriver(new ChromeDriver(), LogLevel.Debug);
) and get debug messages as well:
12:32:39 Executing : Navigating.
12:32:39 Debug : Navigating to ULR 'http://damberg.one'.
12:32:40 Executing : Navigated to URL 'http://damberg.one'.
12:32:40 Debug : Identifying element by 'By.XPath: //a'.
12:32:40 Executing : Clicking element aElement.
12:32:40 Info : Retrieving title 'Diverse@damberg.one'.
12:32:40 Debug : Closing IWebDriver instance.
12:32:40 Executing : Switching window/tab/dialog.
12:32:40 Debug : Retrieving window handles.
12:32:40 Executing : Switching to window with name 'CDwindow-11E45A9B9F0F4C93573C04D48AFF5598'.
12:32:41 Info : Retrieving title 'CV'.
12:32:41 Debug : Closing IWebDriver instance.
Verifications
If you also want Asserts being suitable for system level testing and logging, please look into the TAF framework.
Pre-requisites and usage:
You'll need a reference to Selenium of-course. Then create a new class called LoggingWebDriver and copy the code from the class below into this file.
In your tests using Selenium, wrap your driver instance creation with a LoggingWebDriver()
and you'll be good to go.
Logging class to be copied to your code base
Link to code(Sorry for not providing the code in a better format, but I didn't want to spend time solving HTML conversion of code brackets, quotation marks and stuff for the code prettifyer.)
It's all in one single class. Selenium performance should be virtually un-affected and since the code is in your own code repository you can alter it at will.
Using the logging features
Enable logging
The class is meant to wrap any C# IWebDriver instance to provide logging for the features:
var driver = new LoggingWebDriver(new ChromeDriver());
The LoggingWebDriver is a IWebDriver, just like any other Selenium driver. You can use it like you would with your ordinary scripts, but this time it performs logging.
If you want to access your original driver you can reach it by the NonLoggingWebDriver
property of the driver, like this:
driver.NonLoggingWebDriver.FindElement(By.Id("submit")).Click();
Whatever you do with the NonLoggingWebDriver
is not being logged.
Set logging level
To set logging level, either do it through the constructor, or by using the SetLoggingLevel()
method.
Constructor:
var driver = new LoggingWebDriver(driver: new ChromeDriver(), logLevel: LogLevel.Debug);
Inline:
driver.SetLoggingLevel(LogLevel.Debug);
Logging levels are:
- Debug
- Info
- Executing
- Exception
- None
Element naming
An attempt is made to name web elements interacted with, to be recognised in the log. This mechanism is very coarse and only exist to make exection flow understandable.
Creating a custom logger
The default logger writes to console depending on the LogLevel
.
If you want to implement your own logger you'll need to implement the ISeleniumLogger
interface and provide the LoggingWebDriver
instance with this. Using a custom ISeleniumLogger
can be done through the constructor. Multiple concurrent loggers are supported by the AddAdditionalLogger()
method.
var driver = new LoggingWebDriver(new FirefoxDriver(), new CustomSeleniumLogger());
driver.AddAdditionalLogger(new SeleniumSplunkLogger());
A custom logger could look like this:
public class LogExceptionsToDisk : ISeleniumLogger {
public void Log(LogLevel logLevel, string message){
if(logLevel != LogLevel.Exception) return;
Helper.SaveToDisk(message);
}
public void Log(Exception e){
Helper.SaveToDisk(e);
}
}
Other alterations
You might want to do other alterations, like changing the time format for logging rows, or editing the log messages. Feel free to do that. This code is released with the Apache 2.0 license.