When To Automate

Jun 8, 2014

?There's a veritable plethora of blogs, articles and papers, listing the mistakes commonly made when creating functional/ui automation... and many of these, themselves, contain common mistakes. Here's one I recently found... From an article "Five Common Mistakes and Their Solutions" by Artem Nahornyy:

Starting at the Wrong Time It is a common mistake to begin test automation development too early, because the benefits almost never justify the losses of efforts for redevelopment of test automation scripts after the functionality of the application changes until the end of iteration...

Solution During the development phase, members of a quality assurance (QA) team should spend more time creating detailed manual test cases suitable for the test automation...

Mr. Nahornyy argues that automating too early can waste efforts due to change, but the same argument can be made against his solution. Changes can also lead to waste when writing detailed manual test cases, early in development.

My advice is to start automating as soon as you can. For example, you might write automation code when devs are writing app code. Start with a failing test, and flesh out any page objects needed (or create them if they don't exist). Then create any methods and/or data needed by the tests. This will save time later for manual/exploratory testing, when the code is complete.

Even better is if you can get access to the code in progress. Maybe by accessing your dev's machine, or working from a branch, etc... Then you can start hooking up selectors in your page objects and perhaps even running code. More importantly, you can sniff out early bugs in the html/css/javascript, or even in the ui. This is also the perfect time to ask for css changes to solidify page object selectors. Eg. add a css id to an element to save you from using a more brittle solution. It's WAY easier to get a dev to add an id while they're still in the code, rather than later down the line.

Truth is, I would generally argue that it's a mistake to automate too late, rather than too early. The ability to run even the simplest automated tests during a release can add real value. Kicking the can down the road with automation might at best, bloat your backlog, and at worst, never come to be.

Running Test Suites In Geb

Apr 26, 2014

sweetsMaybe you're looking to run a number of tests against a new build, or perhaps you just can't wait for the overnight Jenkins tests to run. Whatever the reason, running multiple Geb tests/specs from your IDE is a snap, thanks to JUnit's sweet suite functionality.

To the code!

import org.junit.runner.RunWith
import org.junit.runners.Suite

@RunWith(Suite.class)
@Suite.SuiteClasses(\[
    CLSearchSpec.class,
    MoodleSandboxLoginSpec.class,
    WikipediaRecentChangesSpec.class,
\])

public class TestSuite {
}

We use two annotations: @RunWith, to specify the runner to run the suite, and @Suite.SuiteClasses, that specifies an array of tests/specs we'd like to run. Just add any specs you'd like run in the array, with .class appended and separated with a comma.

Lastly, we have our actual class, which is left empty. All you need do is run the suite and all your specified tests will run.

I've added this code to my Geb examples project on GitHub, so you can play along at home!

I Cannot Abide QA Politics

Mar 22, 2014

If you write a blog about testing and use the term "automated checks" in a post, I will look the other way. If you use it twice, I might just roll my eyes and stop reading. But if you use it three or four times in a single post, I'm unsubscribing from your blog forever.

Screencast: Creating Your First Test In Geb

Mar 10, 2014

In this screencast, I walk you through writing your first test using Geb and Spock. We'll create a page object and a spec (test), and then run our new test on multiple browsers.

If you're just getting started, you might want to take a peek at my Geb Quick Start and Project Overview screencasts.

Screencast: Geb Automation Quick Start

Mar 8, 2014

I created a screencast that demonstrates how to quickly get up and running with Geb. This demo assumes you have installed a Java JDK, IntelliJ CE and Safari, and we'll make use of my geb-example project, available on GitHub.

And once you're up and running, have a look at my Project Overview screencast...

Customize Page Navigation By Overriding Geb's getPageUrl()

Feb 23, 2014

gebGeb's page object pattern offers the ability to set a url for a page, that allows you to navigate directly to a page. The url is used when calling the to() method (eg. to myPage).

An application I'm working on includes dynamic elements in its urls; the urls contain a session token, and can include a return page, and/or various content IDs. This makes it essentially impossible to navigate directly to the page. And unless you're testing app navigation, it's better to navigate directly. It keeps your code clean and helps mitigate brittleness.

The solution I've come up with is to parse the current url and use it to navigate directly to my pages. To do this, I needed to override Geb's getPageUrl() (thanks to the Geb user group on this!).

BasePage.groovy

/\*\*
 \* override Geb's getPageUrl method...
 \*
 \* This allows us to navigate directly to pages that have session tokens in the url. To use, create
 \* a static pageName instead of url in your page, and pass it the difference in the url. Then
 \* call to MyPage as you normally would.
\*\*/
String getPageUrl() {
    // if pageName exists, grab the current url and replace pageName in url...
    if(this.class.pageName) {
        getDriver().currentUrl.replaceAll("html#.\*/", "html#/${this.class.pageName}/sessionToken/")
    } else {
        // normal page with url...
        this.class.url
    }
}

In my example above, we override Geb's getPageUrl with our own getPageUrl, added to a BasePage, that we'll extend in our page object. In this new getPageUrl, we want to replace the page name in the url (eg. http://localhost/html#/PAGENAME/...). To do this we grab the current url and just replace the current page name with the target page. All the other elements in the url should be correct.

Now we also need to make sure normal pages continue to operate, so we first test to see if pageName property exists in the page. If not, we execute Geb's normal url code. To get the target pageName, we add it as a static property to the page (instead of url), named pageName. The code would looke something like this:

MyPage.groovy

class MyPage extends BasePage { static pageName = "myPage"

Now we can simply call to MyPage as you would with a page with a normal url. Not too shabby!

A Better Way To Handle Checkboxes in Geb

Feb 1, 2014

checkboxGeb is a great automation tool with a lean API but in some cases, perhaps a bit too lean. Case-in-point, checkboxes. Here's how you handle checkboxes in Geb:

// check checkbox...
picCheckbox.value(true)
// verify checkbox is checked...
picCheckbox.value() == "1"

Not the end of the world but IMO, not good enough. It doesn't read overly well and requiring you to know the value of the checkbox to verify it's checked, is less than ideal. We can do better... by using modules.

Using a module, we can add methods for handling checkboxes, that are, IMO, easier to read and use.

CheckboxModule

package modules

import geb.Module

class CheckboxModule extends Module {
    def check() {
        this.value(true)
    }

    def uncheck() {
        this.value(false)
    }

    def isUnchecked() {
        this.value() == false
    }

    def isChecked() {
        !isUnchecked()
    }
}

Pretty simple; create a module and add four methods to it. Two that let us check or uncheck a checkbox, and two that verify it's checked or unchecked. Because isChecked() would require knowing the value of the checkbox (lame), we simply check that is not, not checked (yeah, it hurts my head too).

Now we add the module to our page object and then use the methods in a test...

CLSearchResultsPage

static content = {
  picCheckbox {module CheckboxModule, $("input", name:"hasPic")}
}

CLSearchSpec

 when: 'filter results with pics'
  picCheckbox.check()
  searchButton.click()

  then:
  picCheckbox.isChecked()

As usual, this code is available on GitHub...