2

I'm creating my first full framework using Selenium and TestNG and recently implemented the ability to have logging enabled which will track every action (before and after).

public class CustomLogs {
    //Set Logging_Level to "Debug" to enable logging
    public String Logging_Level = "OFF";

    public void Log(String message) {
        if (Logging_Level == "Debug")
        {System.out.println(message);
        } else {
        }
    }
}

Then, in my page objects, EVERY action method has a log statement before and after EVERY action.

public class LoginPage extends BrowserConfig {

    final WebDriver driver;
    private static CustomLogs customlogs = new CustomLogs();

    //Elements on Page
    @FindBy(id = "userId")
    private WebElement userNameField;

    //Constructor
    public LoginPage(WebDriver driver) {
        this.driver = driver;
    }

    //Fill Username
    public void FillUsername(String userNameEntry) {
        userNameField.clear();
        customlogs.Log("Entering Username: "+userNameEntry+"...");
        userNameField.sendKeys(userNameEntry);
        customlogs.Log("Username entered.");

It seems like this is very beneficial, not only for debugging, but also because you can now print out every single action step, so keeping records or showing others what steps were completed is very straightforward. But I can also see how this is cluttering the page object code.

Is this a good idea? And/or is there a better way to do this?

1

2 Answers 2

2

Three tips:

  1. For logging this much detail, you can implement the WebDriverEventListener (as dzieciou already said in his comment). A great explanation is found on toolsqa. This will clear up your actual code to a great extent.

  2. If you want other (further) things in your log, consider using helper methods for common actions. This will again greatly reduce duplicate (logging) code. If you ever want to change something to the logging system, you'll have much less refactoring work. For example:

     //Fill Username
     public void FillUsername(String userNameEntry) {
         Helper.fillText(userNameField, userNameEntry);
    
     // static helper method in a separate class
     public static void fillText(WebElement el, String s){
         el.clear();
         log(...);
         el.sendKeys(s);
         log(...);
    
  3. You're using JAVA. Please consider Log4j, which is very flexible in which messages to show. Your Logging_level variable is fine, but Log4j has five default levels (Trace, Debug, Error, ...) and is easily configurable. Worth looking into!

4
  • Log4j is passe and démodé, go for slf4j and logback ;-)
    – dzieciou
    Mar 1, 2016 at 19:37
  • Logback looks interesting... good tip.
    – FDM
    Mar 1, 2016 at 19:52
  • Thank you everyone for the feedback. I think I'll start with the WebDriverEventListeners since they are basically accomplishing what I was trying to do and can be abstracted out into their own class. I'll also start looking into log4j and slf4j in case I need further logging. Mar 1, 2016 at 21:46
  • Is there no alternative than to have to replace element.click() with utilityClass.click(element)? Just a mess of constantly switching direction of action. Jul 26, 2017 at 19:42
1

Based on the suggestions by others, I started looking into WebDriverEventListener and found this blog post very helpful: Seleniumworks: EventFiringWebDriver

After implementing this, I have the following code, which seems to work for my needs. (Still need to work on log4j though)

A class for implementing the WebDriverEventListener -

package CommonMethods.Listeners;

    import org.openqa.selenium.*;
    import org.openqa.selenium.support.events.AbstractWebDriverEventListener;



    public class EventListener extends AbstractWebDriverEventListener {
        private By lastFindBy;
        private WebElement lastElement;
        private String originalValue;



        // Prints the URL before Navigating to specific URL "get("http://www.google.com");"
        @Override
        public void beforeNavigateTo(String url, WebDriver driver) {
            System.out.println("Before Navigating To : " + url + ", my url was: "
                    + driver.getCurrentUrl());
        }

        // Prints the current URL after Navigating to specific URL "get("http://www.google.com");"
        @Override
        public void afterNavigateTo(String url, WebDriver driver) {
            System.out.println("After Navigating To: " + url + ", my url is: "
                    + driver.getCurrentUrl());
        }

        // Called before finding Element(s)
        @Override
        public void beforeFindBy(By by, WebElement element, WebDriver driver) {
            lastFindBy = by;
            System.out.println("Trying to find element: '" + lastFindBy + "'.");
            //System.out.println("Trying to find: " + by.toString()); // This is optional and an alternate way
        }

        // Called after finding Element(s)
        @Override
        public void afterFindBy(By by, WebElement element, WebDriver driver) {
            lastFindBy = by;
            System.out.println("Found element: '" + lastFindBy + "'.");
            //System.out.println("Found: " + by.toString() + "'."); // This is optional and an alternate way
        }


      //CLICK | CLICK()

        // Called before clicking an Element
        @Override
        public void beforeClickOn(WebElement element, WebDriver driver) {
            System.out.println("Trying to click: '" + element + "'");
            // Highlight Elements before clicking
            for (int i = 0; i < 1; i++) {
                JavascriptExecutor js = (JavascriptExecutor) driver;
                js.executeScript(
                        "arguments[0].setAttribute('style', arguments[1]);",
                        element, "color: black; border: 3px solid black;");
            }
        }

        // Called after clicking an Element
        @Override
        public void afterClickOn(WebElement element, WebDriver driver) {
            System.out.println("Clicked Element with: '" + element + "'");
        }


         //* CHANGING VALUES | CLEAR() & SENDKEYS()

        // Before Changing values
        @Override
        public void beforeChangeValueOf(WebElement element, WebDriver driver) {
            lastElement = element;
            originalValue = element.getText();

            // What if the element is not visible anymore?
            if (originalValue.isEmpty()) {
                originalValue = element.getAttribute("value");
            }
        }

        // After Changing values
        @Override
        public void afterChangeValueOf(WebElement element, WebDriver driver) {
            lastElement = element;
            String changedValue = "";
            try {
                changedValue = element.getText();
            } catch (StaleElementReferenceException e) {
                System.out
                        .println("Could not log change of element, because of a stale"
                                + " element reference exception.");
                return;
            }
            // What if the element is not visible anymore?
            if (changedValue.isEmpty()) {
                changedValue = element.getAttribute("value");
            }

            System.out.println("Changing value in element: " + lastElement
                    + " from '" + originalValue + "' to '" + changedValue + "'");
        }
    }

And then my page objects were modified to use this listener -

package Pages.LoginPage;

import CommonMethods.Listeners.EventListener;
import configs.BrowserConfig;
import configs.UserConfigs;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.events.EventFiringWebDriver;


public class LoginPage extends BrowserConfig {

    private static WebDriver driver = BrowserConfig.driver;
    private static EventFiringWebDriver event1 = new EventFiringWebDriver(driver);
    private static EventListener handle = new EventListener();

    //Element Locator Variables
    String userNameFieldById = "userId";
    String passwordFieldById = "passwordId";
    String submitButtonById = "submitId";
    String pageHeaderTitleByTagName = "Health Hero Network";
    String customerSupportLinkByLinkText = "Customer Support";
    String termsOfUserLinkByLinkText = "Terms of Use and Legal Notices";
    String mainImageByXpath = "//img[contains(@src,'images/login/header1.jpg";
    String invalidLoginErrorByClassName = "hhnErrorRedFontBold";


    //Constructor
    public LoginPage(WebDriver driver) {
        this.driver = driver;
    }

    //Fill Username
    public void FillUsername(String userNameEntry) {
        event1.register(handle);
        WebElement userNameField = event1.findElement(By.id(userNameFieldById));
        userNameField.clear();
        userNameField.sendKeys(userNameEntry);
        event1.unregister(handle);
    }

    //Fill Password
    public void FillPassword(String passwordEntry) {
        event1.register(handle);
        WebElement passwordField = event1.findElement(By.id(passwordFieldById));
        passwordField.clear();
        passwordField.sendKeys(passwordEntry);
        event1.unregister(handle);
    }

    //Click Login Button
    public void ClickLoginButton(){
        event1.register(handle);
        WebElement submitButton = event1.findElement(By.id(submitButtonById));
        submitButton.click();
        event1.unregister(handle);
    }

    //Login
    public void LogIn(String usernameEntry, String passwordEntry) {
        FillUsername(usernameEntry);
        FillPassword(passwordEntry);
        ClickLoginButton();
    }

    //Invalid Login
    public void InvalidLogin(String usernameEntry, String passwordEntry) {
        FillUsername(usernameEntry);
        FillPassword(passwordEntry);
        ClickLoginButton();
    }

    //Valid Login
    public  void validLogin() {
        FillUsername(UserConfigs.getValidUsername());
        FillPassword(UserConfigs.getValidPassword());
        ClickLoginButton();
    }

    //Verify Invalid Login error
    public void VerifyInvalidLogin() {
        event1.register(handle);
        try {
            WebElement loginError = event1.findElement(By.className(invalidLoginErrorByClassName));
            if (loginError.isDisplayed()) {
                System.out.println("Invalid login credentials provided");
            } else {
                System.out.println("Test error");
            }
        } catch (NoSuchElementException e) {
            System.out.println("Test error.  Invalid login error message not present");
        } finally {
            event1.unregister(handle);
        }
    }
    //Verify successful logout
    public void VerifyLogout() {
        try {
            FillUsername("");
            System.out.println("Logout SUCCESSFUL");
        }catch (NoSuchElementException e){
            System.out.println("Logout FAILED");
        }
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.