Applitools Eyes

In this section, we will set up a basic Applitools Eyes implementation so that we can see what it can do for us. We will start with the code that we created in Chapter 1Creating a Fast Feedback Loop and Chapter 2Producing the Right Feedback When Failing, and add in our eyes code. You don't need to pay for a full Applitools Eyes subscription to try this out, they offer trial accounts with access to all the functionality (you will need to use either Chrome or Firefox to log into their website, however).

So first things first; let's add a dependency to the Applitools code into our POM:

<dependency>
<groupId>com.applitools</groupId>
<artifactId>eyes-selenium-java3</artifactId>
<version>3.32.1</version>
</dependency>

Next, we will make some modifications to our DriverFactory class to allow us to have an eyes object as well as a driver object stored in it. We will need to add the following variables:

private String currentTestName;
private Eyes eyes;
private final String eyesAPIKey = System.getProperty("eyesAPIKey", "<DEFAULT_KEY_HERE>");
private final Boolean disableEyes = Boolean.getBoolean("disableEyes");

The currentTestName is something that we will use to group the screenshots that eyes takes into screenshots for specific tests. The eyes object is the main control object that we will use to talk to the Applitools server and pass data across to them, as well as performing actions such as taking screenshots of the current screen. 

We then have our eyesAPIKey, which we will need to authenticate with the Applitools server, and finally a Boolean called disableEyes that we can use to, yes you guessed it, disable eyes for the current test run.  This gives us the ability to run our tests without taking screenshots and uploading them to the Applitools server every time. Since this is a paid service there are restrictions on how often you can use it depending on your subscription; as a result we want the ability to turn things off when we are debugging errors so that we don't rattle through our subscription minutes when we are not really interested in comparing screenshots.

Next, we have a few methods that we need to add:

public void setTestName(String testname) {
currentTestName = testname;
}

We created a currentTestName variable to track the name of the current test; obviously we need a way to update it depending on the name of the current test and this small method will give us the ability to do that.

Finally, we have the code that does all the real work:

public Eyes openEyes() throws Exception {
if (null == eyes) {
eyes = new Eyes();
eyes.setApiKey(eyesAPIKey);
eyes.setIsDisabled(disableEyes);
eyes.open(getDriver(), "Google Example", currentTestName);
}

return eyes;
}

This method is similar to our getDriver() method. When we invoke it we see if we already have a valid eyes object or not. If we do, we return it ready for use. If we don't we go about generating one. We instantiate the eyes object and add our API key. We then work out if we really want to start sending data over to the Applitools server or not using the disableEyes flag. 

We still perform eyes.open() in every case because we want to send back a valid eyes object that will prevent us seeing null pointer errors if we have disabled eyes. You will notice that we pass our getDriver() method into eyes.open(), instead of just the driver variable we have stored in the class. 

This is to ensure that we don't inadvertently send in a null driver object, which will prevent us from trying to take a screenshot before instantiating the driver (showing a blank driver screen demonstrating that we haven't loaded a web page is probably better than a slightly confusing null pointer exception). The next option is an app name that allows Applitools to filter tests into relevant buckets. If you are testing multiple apps using the same Applitools account, you can then have buckets of tests related to specific apps. Finally, we pass in the test name so that we know which tests screenshots are related to.

Finally, we need to add a clean up method:

public void closeEyes() {
try {
eyes.close();
} finally {
eyes.abortIfNotClosed();
}
eyes = null;
}

This just attempts to close our connection to the Applitools server. If we can't close it down cleanly we will perform a hard abort to make sure that we don't leave any connections open on our side. Finally, we set the eyes object to null again to guarantee that the next time we call openEyes() we go through the process of instantiating a new object again.

Now that we have made our modifications to our DriverFactory, we also need to tweak our DriverBase.

First of all we need to make sure that we successfully pass our test name into our DriverFactory at the start of each test as it's now going to be used to instantiate our eyes object:

@BeforeMethod(alwaysRun = true)
public static void setTestName(Method method) {
driverThread.get().setTestName(method.getName());
}

TestNG makes this nice and easy for us. Since it has already scanned all the test methods so that it can throw them into one big bucket, it already has some basic information about them. It also provides the ability to pass this information into our own methods if we use the @BeforeMethod annotation. Here, we simply get TestNG to pass in the information it's storing about the method it is about to run, then we extract the name and set it as the current test name in our DriverFactory.

Next we need to provide a way for us to get the eyes object out of DriverFactory and into our test. This works in exactly the same way as our getDriver() method:

public static Eyes openEyes() throws Exception {
return driverThread.get().openEyes();
}

Finally, we need to make sure that when we clean up at the end of our tests we also clean up the eyes object; this is a simple tweak to our closeDriverObjects() method to make sure that it also calls closeEyes():

@AfterSuite(alwaysRun = true)
public static void closeDriverObjects() {
for (DriverFactory webDriverThread : webDriverThreadPool) {
webDriverThread.quitDriver();
webDriverThread.closeEyes();
}
}

We are now ready to add some screen comparison ability into our tests! We already have our basic Google example; let's add in a visual check:

private RemoteWebDriver driver;

@BeforeMethod
public void setup() throws MalformedURLException {
driver = getDriver();
}

private void googleExampleThatSearchesFor(final String searchString) throws Exception {
driver.get("http://www.google.com");

WebElement searchField = driver.findElement(By.name("q"));

searchField.clear();
searchField.sendKeys(searchString);

System.out.println("Page title is: " + driver.getTitle());

searchField.submit();

WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until((ExpectedCondition<Boolean>) d -> d.getTitle().toLowerCase().startsWith(searchString.toLowerCase()));

openEyes().setMatchLevel(MatchLevel.LAYOUT);
openEyes().checkWindow("Search results for " + searchString);

System.out.println("Page title is: " + driver.getTitle());
}

@Test
public void googleCheeseExample() throws Exception {
googleExampleThatSearchesFor("Cheese!");
}

We can now run this test to set up our baseline image.

Every time you run a test and tell Applitools about it, some checks will be performed on the Applitools server. If it can't find any record of tests for the selected app and test name, it will assume that it is a new test and any screenshots that are taken will become the baseline for subsequent runs. As a result you need to be aware that if you change test names Applitools will think you want to create a new baseline. Be careful about changing test names; you may miss a visual change because a new baseline has been created instead of a comparison with your old screenshot.

You can log into the Applitools website and you will now see something like the following:

This is our initial baseline and all subsequent tests will be compared against this. Let's run the same test again.

This time, you will most likely see something like the following:

If you click on the thumbnail, you will be shown a bigger version of it so that you can work out exactly what went wrong:

As you can see, Google tells you how long it took to process the search. Since that number changes depending on a series of things (server load, network performance, if the search is cached, and so on) it's unlikely that you will always get the same number. So, we now need to mark this area as something that is known to change.

Select X and draw a box around this area so that Applitools knows it should ignore it, and then click on the thumbs-up icon and save it: 

You will see that the test now goes green. Let's run it again to check that everything is working and that we aren't going to have any problems with subsequent runs of this test:

This time you will see that, as expected, the test has stayed green. If you look at the screenshot, you will see that the number showing how long it took to process the search has changed yet again. Everything is working excellently. We have a visual validation on a website and we have made sure that changes to page speed aren't classified as failures.

Now we are really just scratching the surface here, but I'm sure you can already see just how powerful this enhancement to Selenium is. When I first tried using this tool I found that I could remove loads of assertions from my code, checking that various things were displayed to the user. I don't need those assertions any more, I just need a screenshot that highlights any discrepancies. A tool like this can drastically cut down on the number of lines of code in your tests and that can only be a good thing.  

Where Applitools Eyes really excels is mobile testing. Appium is great, but it can still be very slow depending upon the device you are running tests against. If you have 10 assertions on a page to check that various controls exist, things can really slow down. Replacing those 10 assertions with a single screenshot taken by Applitools Eyes that can check everything on the screen can really speed your tests up.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.244.216