What “Passengers” Can Teach Us About Quality Assurance

Last weekend, I watched the movie Passengers. The basic plot of the movie is that two passengers in hibernation on a flight from Earth to another planet are awakened ninety years too early. As a QA engineer the movie got me thinking about two valuable lessons for developing and testing software.

Lesson One: “And Yet It Did”
In Passengers, when Jim’s hibernation pod fails, he tells the ship’s computer, the android bartender, and even another human what has happened. The response of all three is “Well, that’s impossible. The hibernation pods never fail.” Jim’s response is “Then how do you explain the fact that I’m here?” Many times in my testing career I have been told by developers that the behavior I am observing in our software is impossible. And I always respond with “And yet, here is the behavior that I’m seeing”. In one particular instance at a previous company, I was testing that information entered into the third-party software we integrated with was making it into our software. This testing was going well, until one entry didn’t travel to our software. I told the developer about it. He said, “That’s impossible. I’ve tested this, and you’ve been testing this for days.” I said, “Yes, and yet, this record wasn’t saved.” He said, “Look at the logs- you can see that the information was sent.” I said, “Yes, and yet, it wasn’t saved.” He said, “I entered more information just now, and you can see that it was saved.” I said, “Yes, and yet, the information I entered was not saved.” After much investigation, it was finally discovered that there was a bug where the database was not saving any record after the 199th record. Because I was testing in a different table than he was, and he didn’t have as many records, he didn’t see the error. The moral of the story: Even if something is impossible, it might still happen.

Lesson Two: “But What If It Did?”
One of the scariest parts of Passengers for me was that there was no way for Jim to reboot his hibernation pod and return to hibernation. Also, there were no spare pods. Even worse, there was no way for him to wake up the captain or any human who could help him. I found myself yelling at the screen, “How is this even possible? Why didn’t they put in contingency plans?” The answer, of course, is that the designers of the system were SO SURE that nothing could ever go wrong with the pods. But something did go wrong, and due to their false confidence there was no way to make it right. QA engineers are always thinking about all the possible ways that software can fail. I have often heard the argument “But no sane user would do that.” And I always respond with “But what if they did?” While we may not have time to account for every possible way that our software might fail, we should create appropriate responses for as many ways as we can, and log the rest for future fixes.

I like to think that somewhere on Earth in the Passengers universe, a QA engineer is saying to her product owners at the spaceship design company, “See, I TOLD you the pods could fail!”

Ask Your Way to Success

Ten years ago, I didn’t know how to use a Windows computer.  I didn’t know how the file system worked.  I didn’t know what right-clicking on a mouse did.  Today I am a QA Engineer doing both manual and automated testing at a great company.  How did I get here from there?

I asked a lot of stupid questions.

Most people are reluctant to ask questions, because they are afraid to look ignorant.  But I maintain that the best way to learn anything quickly is to ask questions when you don’t understand what’s going on.
Here are six ways that asking questions improves your knowledge and the health of your company:

1. Questions give others an opportunity to help you, which helps them get to know you better and establishes a rapport.  At my first official QA job, I was working with hotshot developers, all of whom were at least a decade and a half younger than me.  It was embarrassing having to admit that I didn’t know how to reset a frozen iPhone or find the shared drive in File Explorer, but I asked those questions anyway, I remembered the answers, and I showed my co-workers that I was a fast learner.

2. Questions help developers discover things they may have missed.  On countless occasions where a developer has been demonstrating a feature to me I’ll ask a question like “But what if there are no records for that user?”, or “What if GPS isn’t on?”, and they will suddenly realize that there is a use case they haven’t handled.

3. Questions keep everyone honest.  I have worked with other QA engineers who bandy about terms like “back-end call” or “a different code path” without actually knowing what they are talking about.  Asking what they mean by what they are saying makes sure that they do the work to find out what they are actually testing.  And when they get their answers, I get my answers as well.

4. Questions give you an opportunity to clear things up in your head.  You may have heard the expression “Rubber duck debugging”; I think this method works well when you’re asking questions.  I have found that sometimes just formulating the question out loud while I’m asking it clears things up for me.

5. Questions clarify expectations.  Yes, sometimes I have felt dumb saying things like “You want me to test this on the latest build, right?”, but every now and then I discover that there’s been a miscommunication, and I’d much rather find out about it before I start testing rather than after I’ve been testing the wrong thing for an hour.

6. Questions clarify priorities.  There have been many times where I’ve asked “Why are we adding this feature?”  There is almost always a good reason, but the discussion helps the team understand what the business use case is, which helps the developers decide how to design their solution.

A caveat: Don’t ask questions that you can find the answers to by using a search engine (example: “How do I find the UDID of a device using iTunes?”) or by going back and reading your email (example: “What day did we decide on for code freeze?”).  Asking these types of questions results in wasted time for everyone!

In summary, asking might make you feel silly in the short run, but it will make you and your team much smarter in the long run.  And hopefully it will create an atmosphere in which others feel comfortable asking questions as well, improving teamwork for everyone!

Fix All the Things

It’s very easy when you are rushing to complete features to let some bugs slide.  This article will show why in most cases it’s better to fix all the bugs now rather than later.  The following scenario is hypothetical, but is based on my experience as a tester.  

NewTech Inc. is very excited about offering a new email editor to their customers.  Customers will be able to compose emails to their clients and schedule when they should be sent from within the NewTech app.  NewTech’s service reps will also have the ability to add or change a company logo for the customers.  
Because the feature is on a deadline, developers are rushing to complete the work.  The QA engineer finds an issue: when changing the company logo, the logo doesn’t appear to have changed unless the user logs out and back in again.  The team discusses this issue and decides that because customers won’t see the issue (since it’s functionality that only NewTech employees can use), it’s safe to let this issue go on the backlog to be fixed at another time.
The feature is released, and customers begin using it.  The customers would all like to add their company logo to their emails, so they begin calling the NewTech service reps asking for this service.  The service reps add the logo and save, but they don’t see the logo appear on the email config page.  The dev team has forgotten to let them know that there’s a bug here, and that the workaround is to log out and back in again.  

Let’s assume that each time a service provider encounters the issue and emails someone on the dev team about it, five minutes is wasted.  If there are ten service reps on the team, that’s fifty wasted minutes.
Total time wasted to date: Fifty minutes

But now everyone knows about the issue, so it won’t be a problem anymore, right?  Wrong!  Because NewTech has hired two new QA engineers.  Neither one of them knows about the issue.  They encounter it in their testing, and ask the original QA engineer about it.  “Oh, that’s a known issue,” he replies.  “It’s on the backlog.”  Time wasted: ten minutes for each new QA engineer to investigate the problem, and ten minutes for each new engineer to ask the first QA engineer about it.
Total time wasted to date:  One hour and ten minutes

Next, a couple of new service reps are hired.  At some point, they each get a request to from a company to change the company’s logo.  When they go to make the change, the logo doesn’t update.  They don’t know what’s going on, so they ask their fellow service reps.  “Oh yeah, that’s a bug,” say the senior service reps.  “You just need to log out and back in again.”  Time wasted: ten minutes for each new service provider to be confused about what’s going on, and ten more minutes in conversation with the senior service reps.
Total time wasted to date: One hour and thirty minutes

It’s time to add new features to the application.  NewTech decides to give their end users the option of adding a profile picture to their account.  A new dev is tasked with adding this functionality.  He sees that there’s an existing method to add an image to the application, so he chooses to call that method to add the profile picture.  He doesn’t know about the login/logout bug.  When one of the QA engineers tests the new feature, she finds that profile picture images don’t refresh unless she logs out and back in again.  She logs a new bug for the issue.  Investigating the problem and reporting it takes twenty minutes.
Total time wasted to date:  One hour and fifty minutes

The dev team meets and decides that because customers will see the issue, it’s worth fixing.  The dev who is assigned to fix the issue is a different dev from the one who wrote the image-adding method (who has since moved on to another company), so it takes her a while to familiarize herself with the code.  Time spent fixing the issue: two hours.  
Total time wasted to date: Three hours and fifty minutes

The dev who fixed the issue didn’t realize that the bug existed for the company logo as well, so didn’t mention it to the QA engineer assigned to test her bug fix.  The QA engineer tests the bug fix and finds that it works correctly, so she closes the issue.  Time spent testing the fix: thirty minutes.
Total time wasted to date: Four hours and twenty minutes

When it’s time for the new feature to be released, the QA team does regression testing.  They discover that there is now a new issue on the email page: because of the fix for the profile images, now the email page refreshes when customers make edits, and the company logo disappears from the page.  One of the QA engineers logs a separate bug for this issue.  Time spent investigating the problem and logging the issue: thirty minutes.
Total time wasted to date: Four hours and fifty minutes

The dev team realizes that this is an issue that will have significant impact on customers, so the developer quickly starts working on a fix.  Now she realizes that the code she is working on affects both the profile page and the email page, so she spends time checking her fix on both pages.  She advises the QA team to be sure test both pages as well.  Time spent fixing the problem: one hour.  Time spent testing the fixes: one hour.
Total time wasted to date: Six hours and fifty minutes

How much time would it have taken for the original developer to fix the original issue?  Let’s say thirty minutes, because he was already working with the code.  How much time would it have taken to test the fix?  Probably thirty minutes, because the QA engineer was already testing that page, and the code was not used elsewhere.  
So, by fixing the original issue when it was found, NewTech would have saved nearly six hours in work that could have been spent on other things.  This doesn’t seem like a lot, but when considering the number of features in an application, it really adds up.  And this scenario doesn’t account for lost productivity from interruptions.  If a developer is fielding questions from the service reps all day about known issues that weren’t fixed because they weren’t customer-facing bugs, it’s hard for him to stay focused on the coding he’s doing.

The moral of the story is: unless you think that no user, internal or external, will ever encounter the issue, fix things when you find them!

How to Train Your Dev

Training your Dev is really about training yourself.  A more accurate (but much less catchy) title for this blog post would be “How to work and communicate effectively in order to facilitate a productive relationship with developers”.  
There are two steps to having a good working relationship with your dev: 1) developing good work habits, and 2) communicating clearly.  We’ll take a look at these two steps in detail.
Good Work Habits:
  • Make sure you have completely read a feature’s Acceptance Criteria and all available notes and documents.  This can help prevent unnecessary and time-consuming misunderstandings.
  • Ask questions if there is anything in the feature that you don’t understand.  Don’t make potentially incorrect assumptions.
  • Document your work.  This is especially helpful when you have found an issue and the developer needs to know what browser you were using or what server you are pointing to.
  • Check twice to make sure that you really have a bug.  Perhaps what you are seeing is a configuration problem, a connection problem, or simply user error.
Communicating Clearly:
  • Learn the preferred communication style of your dev and use it.  For example, some developers like to hear about issues immediately, and testing and bug fixing become a collaboration.  Other developers prefer to hear right away only if the issues are big ones, and would rather have you document the smaller issues for a later conversation.
  • Ask your dev to walk you through any confusing features.  He or she will be happy to explain things to you, because they know that any information they give you at the outset of testing will save misunderstandings later.
  • Be kind when reporting issues.  Your dev has worked hard on the feature he or she has delivered, and we all know it’s no fun to have our work criticized.
  • Give feedback in the form of a question.  This can soften the blow of finding a bug.  For example, “I noticed that when I clicked the Save button, I wasn’t taken to the next page.  Is this as designed?”
  • Let your dev know what he or she can do to help you do your job more efficiently.  A good example of this is asking them to not assign an issue to you until it is actually in the test environment, so you won’t inadvertently start testing it before the code is there.
A good working relationship with your dev is all about trust!  You trust that your dev has completed the work they’ve assigned to you, that they’ve done some of their own testing before the handoff, that the work is in the QA environment and ready for testing, and that your dev has let you know about any potential areas of regression to test.  
In turn, your developer trusts that you have tested everything in the Acceptance Criteria, that you’ve done regression testing, that you’ve tested with various security levels and on various browsers, that the issues you’ve found are legitimate, and that you will clearly communicate what you tested and what issues you found.  

Train yourself to work effectively and communicate clearly, and you will find this level of trust in your relationship with all the developers you work with!

The Trouble With Toggles

Some of you may have seen the classic Star Trek episode, “The Trouble with Tribbles”.  In the episode, Lt. Uhura gets a gift of a cute little ball of fur from a passing trader.  It’s a huge hit with the crew, and when the tribble has babies, everyone who wants one can enjoy these little fuzzy creatures.  The trouble begins when they discover that the tribbles are born pregnant and quickly give birth, increasing the tribble population exponentially until they are taking over the entire ship. 

Today I would like to talk about the trouble with toggles, and what they have in common with tribbles.  Toggles can be helpful to developers in many ways.  They make it easier for development of a new feature to continue in the main branch, without disturbing the work of others.  They enable us to do beta testing, where a handful of customers try out a feature or layout and provide their feedback while most customers continue using the software without the changes.  And they enable us to quickly remove a feature if it’s discovered that it has a critical error.  But toggles can be problematic for two very important reasons, which I will explain using our tribble friends.

First, toggles make more work for QA engineers.  Just as the tribbles multiplied exponentially, the number of test passes we need to do when using toggles also multiplies exponentially.  Consider the following scenario: Team One adds a new feature with a toggle.  In order to make sure that the feature hasn’t broken any existing functionality, they need to do two passes of smoke tests: one with the toggle on, and one with the toggle off.  Next they add a second feature with a toggle.  Now there are four different combinations of scenarios that their software can have: both toggles on, toggle A on and toggle B off, toggle B on and toggle A off, and both toggles off.  So they’ll need to do four different passes of smoke tests to make sure that all their features are working correctly.  Now let’s add in Team Two.  They have two toggles as well, so they’ll need to do four passes of smoke tests for all the permutations of toggle A and toggle B. 

As release day approaches, Team One and Team Two are merging their code into the main branch.   Now there are EIGHT different possible combinations for toggle states. Even if the teams decide that it’s a low risk to skip smoke testing in all eight scenarios, there will still be a lot of investigative work involved for every bug found.  Let’s say someone finds a bug in an instance where Toggle A and C are on and Toggle B and D are off.  They’ll need to answer questions like “What if just Toggle C is on?  Do we still see the bug? What about if Toggle D is turned on as well?  Does the bug go away?”  Research like this can be really time-consuming.  This is time that could be better spent doing exploratory testing.  If all the features were enabled in all scenarios, testers would only need to do one pass of smoke tests and spend the rest of their time digging deeper into each new feature.

Secondly, toggles can give software teams a false sense of security, which can lead to buggy software and tech debt.  In “The Trouble with Tribbles”, the tribbles’ purring sound had a soothing effect on human’s nervous systems, which made the crew pay less attention to the fact that in a couple of days the tribble population would fill the Enterprise from deck to ceiling.  Similarly, engineers can be soothed by the knowledge that they can always turn the feature off, which means that they can test the feature less carefully. If bugs are found, they can be dismissed with the statement, “Well, it’s Beta, and the Beta users will understand.  We’ll log it and take a look at it later.” Bugs like these can compound, especially when mixed with all the other bugs in all the other Beta features.  And the bugs can take a lot longer to fix six months later when the original developers don’t remember what they did or may not even be with the company any more. Alternatively, if the teams tested every feature with the knowledge that all users would be seeing it on first release, they would test more carefully and fix bugs right away, ensuring much greater code quality from the outset.

Like tribbles, toggles can be fun, in that they enable us to add more pizazz more quickly to an application.  But as the crew of the Enterprise found, too much of a good thing has its consequences!  

Designing a Complete Automation Suite

In my last two positions, I have been the sole Software Engineer in Test for a brand-new software platform.  This means that I have been able to set up an entire arsenal of automated tests for both the backend and the front-end.  This article will describe the suite of tools I used to test a web application.

API Tests:
First, I set up tests that checked that every API call worked correctly.  I did this through Runscope, an API testing tool I highly recommend.  I was fortunate enough to have engineers who set up Swagger, a framework that lists API calls in a really easy-to-read format.  I used the information from Swagger to set up my Runscope tests.  For testing against our development environment, I created tests for every GET, POST, PUT, and DELETE request.  I set this suite of tests to run once a day as a general health check for the development environment.  For testing against our production environment, I set up tests for every GET request that ran hourly.  This way we would be notified quickly if any of our GET requests stopped working.  Runscope is also good for monitoring the response times of API calls.

Backend Tests:
The backend of our platform was written in Java, and we developed a suite of integration tests using JUnit and AssertJ.  Generally the developers would create a few basic tests outlining the functionality of whatever new feature they had coded, and I would create the rest of the tests, using the strategy that I outlined in my previous post.

Front-End Tests:
The front-end of our platform was written in Angular, so I used Protractor to write a series of end-to-end tests.  The UI developers were responsible for writing unit tests, so I focused on testing typical user interactions.  For example, in my Contact-Info tests, I tested adding and editing a phone number, adding and editing an email address, and adding and editing a mailing address.  If it was possible to delete contact info in our UI, I would have added those tests to the suite as well.  I set up two suites of tests: a smoke suite and a full suite.  The smoke suite was for basic feature testing and was integrated into the build process, running whenever a developer pushed new code.  If they pushed a change that broke a feature, they would be notified immediately and the code would not be added to the build.  The full suite was set to run every test, and was configured in Jenkins to run overnight against the development environment using SauceLabs.  If anything happened to cause the tests to fail, I’d be notified by email immediately.

Since the UI also included uploaded images, I integrated Applitools visual testing into my Protractor tests.  Their platform is extremely easy to integrate and use.  I wrote tests to check that the image I added to a page appeared on the screen, and tests to check that if I changed an image, the new image would appear on the screen.

Load Tests:
In my previous position, I set up a series of load tests using SoapUI.  This tool is similar to Runscope in that it tests API calls, but it has the added functionality of being able to configure load testing.  I set up the following test scenarios: a Baseline test, where a small group of simulated users would run through a series of API requests; a Load test, where a small group of users would make a more frequent series of requests; a Stress test, where a large group of users would make a frequent series of requests; and a Soak test, where a small group of users would make a frequent series of requests for a long test time.  We were able to use this suite of tests to monitor response times, check for memory leaks, and test our load balance setup.

Every application is different, so you may find that you need a different set of tools for your test toolbox!  But I hope that this blog post gives you a jumping-off point to think about how to test your application thoroughly.

Organizing Java Integration Tests

In this post, I’ll be switching gears from discussing automated tests with Webdriver to discussing integration tests that are included as part of an application’s back-end Java code. 

In my new Software Engineer in Test position, I have been writing integration tests to test every API call we have.  This means that I have dozens of test classes, and over a thousand possible individual tests.  Today I’m sharing with you the strategy I came up with for organizing the tests. 

I sorted out the tests into four test types: 

Resource Tests:  these are the “Happy Path” tests- the tests where you expect things to work correctly.  Examples include:  POST requests with valid parameters, GET requests where the correct object is returned,  PUT requests where the new parameters are valid , and DELETE requests where the correct object is deleted.

Security Tests:  this is where I put all the tests that should fail because the user is not logged in.  For every method that is tested in the Resource Tests, there should be a similar test where the method is called, but the user is not logged in.

Validation Tests:  this is where I put all the tests that should fail because a value being sent is invalid.  Examples include: sending an null value where a value is required, sending an empty value where a value is required, sending a value with invalid characters (such as letters in a numeric field), and sending a value with too many or too few characters.  It’s important to include tests for both Create and Update requests. 

Conflict Tests:  this is where I put tests that should fail because the request has been incorrectly formed.  Examples include: 
any POST, GET, PUT, or DELETE request where the required id is null
any POST, GET, PUT, or DELETE request where the required id is non-existent
any POST request where an id of the object about to be created is included
any PUT request where the id in the request does not match the id of the existing object

I created a package for each of these test types.  Then for each domain object, I created four test classes: one for each package.  For example, our code has a domain object called Phone.  Here are the test classes I created:

PhoneResourceTest- verifies that it is possible to add, get, edit, and delete a phone number

PhoneSecurityTest- verifies that it is not possible to add, get, edit, or delete a phone number if the user is not logged in

PhoneValidationTest- verifies that the phone number being added or edited is in a valid format (ten digits, no letters, etc.)

PhoneConflictTest- verifies that it is not possible to add or edit a phone number if the id of the associated contact is missing or incorrect, that it is not possible to add a phone number if there is a phone number id in the request, that it is not possible to edit a phone number if the phone number id in the request does not match the id of the phone number being edited, etc.

I hope this post will help you think about how to best organize your own integration tests!

Finding an Element With Webdriver: Part III

Today we will tackle the most difficult ways to find an element: by css and xpath.  These are the most difficult because it’s so easy to miss a slash or a period in the description.  They are also the most difficult because they are so brittle- every time a developer makes a change to the webpage structure, the navigation path will change and your findElement command will no longer work.

However, finding an Element by css or xpath is often very needed, because developers often don’t take the time to give their elements easy and unique ids or names.  I would recommend using these strategies as a last resort to find the element you need, and I would try css first before resorting to xpath.

First, let’s find an element with css.  Take a look at the table that was described in the last blog post:

<table border=”2″>
<td>Row 1, Column 1</td>
<td>Row 1, Column 2</td>
<td>Row 2, Column 1</td>
<td>Row 2, Column 2</td>

Let’s say you want to find the td element that is in the first column of the second row.  The html doesn’t give you much to work with.  You can’t just do a findElement by tagName, because there are four different elements with the td tag name.  CSS selectors give you a way to navigate to the precise element you want.

In this example, we first want to select the correct row.  We can do this using the nth-of-type descriptor.  Nth-of-type finds a child element where there are more than one child elements in a parent.  In this case, the parent element is the table, and the child element is the row (the tr).  Because the second row is the second tr type in the table, we can describe this second row as tr:nth-of-type(2).
Next, we’ll want to find the first td element in that row.  Because it is the first td element in the row, we can just refer to it as td.  (If we needed the second element, we could describe it as td:nth-of-type(2).)

Now we’ll put these two descriptions together:
findElement(By.css(“tr:nth-of-type(2) td”));

This instruction tells Webdriver to first find a tr element that is the second tr child of its parent, and then within that element, to look for the first td element.

Another way to use css is to find classes.  Let’s say for example that the row we are looking for has this in it: class=”rowwewant”.  We can find classes with css by using a period:
findElement(By.css(“.rowwewant td”);

This instruction tells Webdriver to first find the element with the class name “rowwewant” and then within that element, to look for the first td element.

Now let’s find the same element with xpath.  In order to find the element, we will need to traverse through the DOM to get there.  First we will find the table, then find the second row, then find the first td element:


First the table will be located, then the second row within the table, then the first td element within the row.

The two slashes at the beginning of the xpath indicate that is a relative xpath- the element will be found near the test’s current location.  If this is not effective, an xpath can be created from the root element (see the previous blog for the html for the entire webpage):


There are many more ways that findElement by css or xpath can be used, but hopefully this will provide a jumping-off point for finding elements that can’t be found by easier means.

Understanding the DOM

In order to find and use web elements using By.cssSelector or By.xpath, it is very helpful to understand the DOM.  The DOM (Document Object Model) is simply the interface that is used to interact with HTML and XML documents.  When a JavaScript program manipulates elements on a page, it finds them using the DOM.  If you already understand HTML, it will be very easy to understand how the DOM is organized.  Rather than explaining the differences between the DOM and HMTL source code, I’ll simply direct you to this very helpful webpage:  


If you have little knowledge of HTML, here’s an example of how web elements are organized on a page:
<title>My Web Page</title>
<h1>My Web Page</h1>
<p>This is a paragraph</p>
<a href=”http://www.google.com” >Here’s a link to Google</a>
<table border=”2″>
<td>Row 1, Column 1</td>
<td>Row 1, Column 2</td>
<td>Row 2, Column 1</td>
<td>Row 2, Column 2</td>
If you’d like to see what this webpage would look like, you can simply copy and paste this html code into a notepad file and save it with the .html extension.  Then find the file in the folder where you’ve saved it and double-click on it.  It should open up as a webpage.  
Now let’s take a look at the various elements on the page.  Note that each element has a beginning and ending tag.  For example, the line that says “This is a paragraph” has a <p> tag to indicate the beginning of the paragraph and a </p> tag to indicate the end of the paragraph. Similarly, the title of the page has a beginning tag: <title> and an ending tag: </title>.  
Notice that elements can be nested within each other.  Look at the <table> tag, and then find the </table> tag several lines below it.  In between the tags, you will see row tags (<tr>) and table data tags (<td>).  Everything in between the <table> and </table> tags is part of the table.  Now look at the first <tr> tag and the first </tr> tag.  Notice that there are two pairs of <td></td> tags in between. Everything between the first <tr> tag and the first </tr> tag is a row of the table.  The <td></td> pairs in the row are elements of data in the row.
Now imagine that this data is organized into tree form:
If you were going to traverse the DOM in order to get at the data in Row 1, Column 1, you’d start by finding the <table> element, then by finding the first <row> element, then you’d find the first <data> element.  This is how we will use css selectors and the xpath to find elements in the next blog post.
If you’d like to find out more about HTML and CSS, I highly recommend the w3schools website.  

Finding an Element With Webdriver: Part II

In my last post, I discussed how to find an element with Webdriver using the element id, class name, or name.  In this post, I will discuss how to find an element using link text, partial link text, or tag name.

Return to www.google.com, and look at the links on the bottom of the page.  Right-click on the link that says “Advertising” and select “Inspect Element”.  You should see something like this:
<a class=”_Gs” href=”//www.google.com/intl/en/ads/?fg=1″>Advertising</a>

The link text is what is found between the >reverse brackets<.  In this case, the link text is “Advertising”.

Return to myFirstTest in Eclipse and replace the findElement command in your test with:

This command tells Webdriver to:
1.  Find the element with the link text “Advertising”
2.  Click on the link

If you followed the instructions in the last post, you should still have a breakpoint set in your test. If not, right-click in the left margin of the “driver.quit();” command and select “Insert Breakpoint”. Then run the test by right-clicking on the test and selecting Debug As->JUnit Test.  The test should run and will stop just after it clicks on the Advertising link.  If the test has run correctly, you should now be on the Google Ads page.  Click on the green arrow to finish the test.  

Now we will find an element using a partial link text.  Return to www.google.com, right-click on the link at the bottom of the page that says “Business”, and select “Inspect Element”.  You should see something like this: 
<a class=”_Gs” href=”//www.google.com/services/?fg=1″>Business</a>
The link text is “Business”.  We will find this link using just a portion of the text: “ness”.  

Return to myFirstTest in Eclipse and replace the findElement command in your test with:

Run the test by right-clicking on the test and selecting Debug As->JUnit Test.  The test should run and will stop just after it clicks on the Business link.  If the test has run correctly, you should now be on the Google Business Solutions page.  Click on the green arrow to finish the test.  

Now we will find an element using a tag name.  Go to www.pinterest.com, right-click on the button that says “Continue with Facebook” and select “Inspect Element”.  You should see something like this:
<button class=”Button Module btn hasText large rounded unAuthFacebookConnect registerLoginButton unauthHomeRegisterButton multiStepRedesign” type=”button”>    

Note that the first word that appears after the “<” is “button”.  This is the tag name of the element.

Return to myFirstTest in Eclipse and replace the String URL line with:
String URL = ‘http://www.pinterest.com’
Next, replace the findElement command in your test with:

This command tells Webdriver to:
1.  Find the element with the tag name “button”
2.  Click on the button

Run the test by right-clicking on the test and selecting Debug As->JUnit Test.  The test should run and will stop just after clicking on the button.  If the test has run correctly, you will see a popup prompting you to log in with Facebook.  Click on the green arrow to finish the test.

You have successfully located a web element by using an link text, a partial link text, and a tag name! Before we proceed to finding a web element by css and xpath, it is first necessary to understand the DOM.  This will be the topic of my next blog post.