Don't be afraid of the big bad stack trace

It's surprising how many people are intimidated by stack traces. A reaction that I regularly see when a stack trace appears on screen is panic!

"Oh my god! Something has gone wrong! There are hundreds of lines of text talking about code I don't recognize and I can't take it all in; what do I do?"

The first thing to do is to relax; stack traces have a lot of information but they are actually really friendly and helpful things. Let's modify our project to produce a stack trace and work through it. We are going to make a small change to the getDriver() method in DriverFactory to force it to always return null by using this code:

    public static WebDriver getDriver() { 
        return null; 
    } 

This is going to make sure that we never return a driver object, something that we would expect to cause errors. Let's run our tests again, but make sure that Maven displays a stack trace by using the -e switch:

mvn clean verify -e

This time, you should see a couple of stack traces output to the Terminal; the first one should look like this:

It's not too big, so let's have a look at it in more detail. The first line tells you the root cause of our problem: we have got a NullPointerException. You have probably seen these before. Our code is complaining because it was expecting to have some sort of object at some point and we didn't give it one. Next, we have a series of lines of text that tell us where in the application the problem occurred.

We have quite a few lines of code that are referred to in this stack trace, most of them unfamiliar, as we didn't write them. Let's start at the bottom and work our way up. We first of all have the line of code that was running when our test failed; this is Thread.java line 748. This thread is using a run method (on ThreadPoolExecutor.java line 624) that is using a runWorker method (on ThreadPoolExecutor.java line 1,149), and this carries on up the stack trace. What we are seeing is a hierarchy of code with all the various methods that are being used. We are also being told which line of code in that method caused a problem.

We are specifically interested in the lines that relate to the code that we have written, in this case, the second and third lines of the stack trace. You can see that it is giving us two very useful bits of information, it's telling us where in our code the problem has occurred. If we have a look at our code, we can see what it was trying to do when the failure occurred so that we can try and work out what the problem is. Let's start with the second line. First of all, it tells us which method is causing the problem. In this case, it is com.masteringselenium.listeners.ScreenshotListener.onTestFailure. It then tells us which line of this method is causing us a problem, in this case ScreenshotListener.java:58. This is where our onTestFailure() method tries to pass a WebDriver instance into our writeScreenshotToFile() method. If you look at the next line up, you will see that writeScreenshotToFile() is failing on line 41 where it tries to use the driver instance to get a screenshot and it's throwing a null pointer error.

Now, if you remember, we modified getDriver() to return a null instead of a valid driver object, and obviously we cannot call .getScreenshotAs() on a null, hence the null pointer error.

So why didn't it fail in WebDriverThread? After all, that's where the problem is. Passing null around is quite valid. It's trying to do something with null that causes the problem. This is why it also didn't fail on line 34 of DriverBase. The getDriver() method just passes on a variable, it doesn't actually try to do anything with it. The first time that we tried to do anything with null is when it failed, which was at line 41 of the ScreenshotListener class.

Now if you look closely, you'll notice that although we got a stack trace showing us an error in the ScreenshotListener class, we didn't actually get an error there. This is because we have a try...catch block around the screenshot code; it will report the stack trace so that you can work out why a screenshot wasn't taken, but it doesn't actually fail the test. The errors we actually got were slightly lower down.

If you look a bit further down, you will see that an error was triggered on line 16 of BasicIT.java. This is also a null pointer error, and this line of code is where we first try to use our driver object to do something. This again makes sense.

Finally, we have got yet another null pointer error. This one is in our clearCookies() method, but it's not clear where this has gone wrong, as there is no line number. This is because we have made a mistake: when we wrote our bit of code to clear down cookies between tests to avoid stopping and starting the browser all the time, we didn't take into account the possibility that something may have gone wrong and there may not be a driver object available at this point. This mistake has also resulted in us not attempting to run any of the other tests and we have also got a strange message that three tests were run.

Let's clean this up and stop it causing an error when this happens by using this code:

@AfterMethod(alwaysRun = true)
public static void clearCookies() throws Exception {
try {
getDriver().manage().deleteAllCookies();
} catch (Exception ex) {
System.err.println("Unable to clear cookies: "
+ ex.getCause());
}
}

We have now modified our clearCookies method so that it's surrounded by try...catch. This means that it will capture the error and carry on with the rest of the test run. We are printing some information out to the console so that we can work out what happened, but we have gone for something that's not quite as verbose this time. Rather than print the whole stack trace, we are just going to print the cause. This way, we can still work out what has gone wrong, but we won't have a big stack trace catching our eye and distracting us from stack traces that have actually caused real errors. Let's tweak our ScreenshotListener as well to make the stack trace a little less verbose by using this code:

@Override
public void onTestFailure(ITestResult failingTest) {
try {
WebDriver driver = getDriver();
String screenshotDirectory =
System.getProperty("screenshotDirectory",
"target/screenshots");
String screenshotAbsolutePath = screenshotDirectory +
File.separator + System.currentTimeMillis() + "_" +
failingTest.getName() + ".png";
File screenshot = new File(screenshotAbsolutePath);
if (createFile(screenshot)) {
try {
writeScreenshotToFile(driver, screenshot);
} catch (ClassCastException
weNeedToAugmentOurDriverObject) {
writeScreenshotToFile(new Augmenter()
.augment(driver), screenshot);
}
System.out.println("Written screenshot to " +
screenshotAbsolutePath);
} else {
System.err.println("Unable to create " +
screenshotAbsolutePath);
}
} catch (Exception ex) {
System.err.println("Unable to capture screenshot: "
+ ex.getCause());
}
}

Again, we are now just logging the cause, so let's rerun our tests and see what the output looks like this time. Take a look at this screenshot:

This time, we can see the same errors, but it's a lot easier to understand now that we have cleaned up our code. This time, it's very clear that the problem is on line 16 of our BasicIT.java. If we look at that line of code, we will see that this is the first time that we try to do something with the driver object that we got using the getDriver() method. The error is NullPointerException and this makes perfect sense due to the change that we put in to the cause errors in the first place.

Stack traces can seem scary, but when they are explained, they normally end up being quite obvious. It does however takes a while to get used to reading stack traces and using the information they give you to work back to the core problem. The important thing to remember with stack traces is to read them in full. Don't be scared of them, or skim through them and guess at the problem. Stack traces provide a lot of useful information to help you diagnose problems. They may not take you directly to the problematic bit of code, but they give you a great place to start.

Try causing some more errors in your code and then run your tests again. See whether you can work your way back to the problem you put in your code by reading the stack trace.
..................Content has been hidden....................

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