Zington Z logo

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:

  1. Debug
  2. Info
  3. Executing
  4. Exception
  5. 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.