Monday, December 12, 2011

31 Days of Testing—Day 12: Functional Test 101

In my last post I gave you a bit of a drive-by spewing of some of the things I think are critical to keep your automation maintainable, particularly in the functional test world.

Now let’s take a look at what a functional UI test looks like. Again, there’s a huge difference in the specifics between platforms and frameworks/tools. I’m going to focus on a very simple web UI test to kick off here. I’m using Selenium 2.15. Selenium’s an open source web testing framework available on nearly every platform you could care to write code for. Watir for Ruby is another example of an open source web testing framework. Test Studio, the commercial product I’m involved with, has a free testing framework as its underpinnings.

These tools are used in stand alone tests, but they’re also used extensively as backing for other specification-style testing tools such as Cucumber, Fitness, Capybara, and others.

All these frameworks use roughly the same approach to testing your web applications:

Start up a browser

Navigate somewhere

Find things of interest on the page to interact with

Inspect text

Input text

Click

Scroll

Blow up

(I may be exaggerating with the Blow Up part.)

Without further ado, let’s take a look at how a functional test with Selenium is built. For simplicity’s sake and readability I’m leaving this test a bit more linear than I normally would. I’ll show you some potential refactorings/optimizations in later posts—including some discussion of the Page Object Pattern I’ve mentioned previously.

You may need a separate test framework to actually execute your functional framework inside of. Selenium doesn’t come with any form of test runner, reporting engine, or even Assert support. The examples below use NUnit to drive the tests around

First test first. Skipping over setting up the project and references, the first thing you’ll need to do is declare a test fixture, plus a variable for the browser we’ll be working with. Selenium supports many different browser drivers, all of which implement the IWebDriver interface.

[TestFixture]

publicclass Home_page_displays_correctly

{

IWebDriver browser;

Now we can use a fixture setup method to instantiate our browser. I’m using the FireFox driver, so there are a few profile and binary executable file things I need to deal with. NOTE: In a true implementation, I’d have this instantiation handled by some sort of provider so I could flexibly stand up Firefox, Chrome, IE, or whatever browser I wanted this run to be with.

The last step is to actually navigate to the URL/page we’re going to work with. You need to understand that the navigation will cause WebDriver to wait for the page’s “onload” event to fire. This is a handy blocking step, meaning you don’t need to worry about adding in code to pause and wait for the page to load. You also need to understand this does not have anything to do with dynamic content. You’ll need to handle that yourself. More on that later.

We’ve navigated to the home page, and now it’s time to find the elements we want to test for. Each framework is subtly different, but the general idea is the same: specify an HTML ID, CSS selector, XPath, or some other form of find logic so the framework can locate the element you want. In our WebDriver test we do the following:

[Test]

publicvoid Login_link_is_correct()

{

IWebElement loginLink = browser.FindElement(By.Id("login_link"));

Assert.IsTrue(loginLink.Text.Equals("Login"));

Assert.IsTrue(loginLink.GetAttribute("href").Contains("/login"));

}

We’re grabbing the element with the ID of “login_link”, then we’re checking text and attribute values on that element. We’re using NUnit Asserts to make the actual test.

How do we know which IDs to work with? First, best answer: talk with the developers who created that UI. Second, nearly as good an answer: use a tool like Firebug, IE Developer Toolbar, or the DOM Explorer in Test Studio to help you walk through the page’s DOM.

We can also interact with elements on the page in the same fashion. If I want to log in, I’ll use these steps right after the initial navigation step:

browser.FindElement(By.Id("login_link")).Click();

browser.FindElement(By.Id("username")).SendKeys("testuser");

browser.FindElement(By.Id("password")).SendKeys("abc123");

browser.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));

browser.FindElement(By.Id("login_button")).Click();

I can interact with elements by first finding them, then using sensible commands like Click() for buttons/links, or SendKeys for input fields.

The fourth statement in this group sets an implicit wait for the navigation after the final Click() event on the login button. While WebDriver nicely waited for the page to completely load after the Navigate call above, it doesn’t know to wait for a page load after clicking a link. We’re putting in an implicit wait to have Selenium pause before failing subsequent actions after that navigation.

In this case, that subsequent action is another test validating the presence of the logoff link.

[Test]

publicvoid Logout_link_displays()

{

Assert.IsTrue(browser.FindElement(By.LinkText("Logout")).Displayed);

}

Without the implicit wait, WebDriver would blow past this assert, failing it because the page hadn’t properly loaded.

Implicit and explicit waits are one of the most critical things an automation developer needs to understand. The Selenium documentation has a great article on it which you need to read and understand—you’ll save yourself a lot of pain!

This brief overview showed some common themes regardless of the automation framework you’re using. You need to create a browser, have it navigate somewhere. That navigation may or may not handle delays necessary for your testing. Once you’re at a page you can find and interact with elements on that page.

I’ll follow this with a post on dealing with some common troublesome issues like AJAX or other dynamic content, pop up windows, file system dialogs, and others.

1 comment:

I was having a chat with my friend who is a beginner in testing. He was sharing his problem with me on how to initiate testing. Although there are many books and articles on theories and concepts of testing but there is no help or information available on how to start testing and how a tester should proceed with the same. This is one excellent article you put together.

also if you do not mind here is another article which talks about the basic steps of functional testing:

About Me

I'm the owner/principal of Guidepost Systems. I help lots of great folks figure out what works and what doesn't in the world of delivering quality software -- something I'm very passionate about. I'm also a Father trying to remain sane while trying to build great software, herd my kids around, fix school lunches and handle the yardwork. (And roast great coffee!)