Self-Logging Selenium WebDriver Tests

Image for post
Image for post
Photo by Markus Spiske on Unsplash

One of the characteristics of a good Selenium test automation project is the ability of troubleshooting quickly potential problems.

This means finding fast

  • the method (or assertion) that caused the issue
  • what happened before the code execution stopped
  • what is the last visited web page
  • what are the exception details.

The method that caused the issue is visible in the stack trace generated when an assertion fails or an unexpected exception is thrown.

Stack trace shows also the name of the failed test and all methods that lead to the failed method.

Screenshots help seeing the page where the execution stopped.

In addition to all these details, it is useful to have a log that shows everything that the automated test did from the moment it started until it stopped.

Which brings us to logs.

There are multiple types of logs that can be used in a Selenium project.

  • Selenium Logs
  • Exception Logs
  • Test Execution Logs

I will cover the Selenium and exception logs in other posts.

The type of log that I will focus on here is the test execution log.

The execution logs are useful for seeing everything that the automated test executed before it finished successfully or with an error.

There are multiple ways of generating them:

  • by adding a logging call to each page class method
  • by using an EventFiringDriver
  • by using HtmlElement classes

We will go over each option separately with a very simple automated test for the following test case:

  • open the home page of www.autotrader.ca
  • select a vehicle make
  • select a vehicle model
  • select a price
  • enter a postal code
  • execute the search
  • verify that on results page, the title is correct

Add a logging call to each page class method

This type of logging is done at the page object method level.

This is the code of the test class:

There are 2 things to notice in this test class.

First, the logger object (of the Logger class) is injected in the homePage object which passes it later to the resultsPage object. homePage and resultsPage take care of their own logging.

Second, a CustomAssert class is used so that assertion info is logged before each assertion executes.

The Logger class is very basic. It implements an interface that defines 3 methods:

This is the Logger class implementation:

This simple logger logs text to the console. I could have implemented it in many other ways, by relying on logging frameworks, but this is not what I am interested in.

What I want to do is to implement the execution logging in the simplest way with no code duplication.

The HomePage class follows:

The logging is done in each public method of the HomePage class.

Since there is logging to be done as well in the ResultsPage class, the logger object is also injected in the resultsPage object in the executeSearch() method.

ResultsPage is similar to HomePage:

The page classes log the executed methods.

CustomAssertion does the same for the assertions:

After running the automated test, the results look as follows:

This is the most obvious way of logging everything for the automated test execution.

It is not efficient to add the logging() code in each page object method. This is code duplication which we should avoid as much as possible.

Ideally, we would have logging done with no logging code added to the page classes.

This is hard to do for the page methods, but possible for the interactions with the web elements.

The second option relies on a class from the Selenium WebDriver API.

Logging with EventFiringDriver

This type of logging is done at the action level (find element, click element, test started, test stopped, etc).

The test class is a bit simpler in this case because the SetUp and TearDown methods are moved to a BaseTest class:

No logging object is used in this case as a parameter for the homePage object.

The BaseTest class follows:

The EventFiringDriver object is created and configured in the SetUp() method by wrapping the original driver object. Event handlers are added to the EventFiringDriver object for each event that EventFiringDriver understands:

  • ExceptionThrown
  • ElementClicked
  • ElementValueChanged
  • FindElementCompleted
  • Navigated
  • ScriptExecuting
  • ScriptExecuted

Each event handler gets a method as a parameter:

When Selenium code throws an event (example:ElementClicked), the method associated with the handler of the event is executed automatically.

These methods do the logging to the console based on their parameters.

These parameters can be a locator, an element, an exception, a test or an exception.

Since all logging is done by the event handler, the page classes do not have any logging code:

There is no logging in the HomePage class.

There is no logging in the ResultsPage class either:

No logging code is used in ResultsPage either.

All logging is done by the EvenFiringDriver based on the argument of the event handlers.

The result of executing the test is below:

This result is better and worse than the previous one.

Better because there is no logging code in the page classes.

Worse because the report is hard to use.

When elements are clicked or they change value, the element id is displayed which is totally obscure.

When elements are found, the element locator is displayed which may or may not be useful to point to the element, depending on how complicated the locator is.

So this leads us to the third solution.

Use HtmlElement classes

This type of logging happens as well at the action level.

Instead of using the EventFiringDriver, we create custom Html classes for each type of element (list, button, link, textbox, etc) and make them responsible for logging their methods.

For interacting with a listbox, we need a custom select class:

The CustomSelect class extends the Select class.

It encapsulates a web element and the name of the element.

It creates a Select object from the web element and relies on it for all list operations.

After each operation takes place, logging is being done.

For all other web elements, a custom html element class is used by implementing the IWebElement interface:

The page classes then rely on the custom element classes:

All methods use HtmlElement instead of IWebElement and CustomSelect instead of Select to that logging is done before element methods are executed.

ResultsPage is similar:

In this case, we do not have any explicit logging in the page methods and no EventFiringDriver.

There is no code duplication either.

The result of executing the test is below:

The logging information is much easier to understand and we still do not have any logging code in the page classes.

Using custom classes for each type of html element helps creating self-logging Selenium tests.

These tests log their execution automatically, without any logging code added to the test or to the page classes.

Written by

Blogs about Selenium and Java at https://seleniumjava.com.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store