Make Your Test Automation Groovier

Sergey Pirogov

Sergey Pirogov
    Senior Software

    Test Automation Engineer
    EPAM Systems
    

    skills: ['Java' , 'Groovy']
    blog: http://automation-remarks.com
    twitter: @s_pirogov
    conference: QA Fest, SQA days, SeleniumCamp    

Is your test automation written in statically compiled language?

We want effective Java Test Automation!

Boilerplate

What's the problem?

We live in dynamic world

We test dynamic web applications

Time to use dynamic languages!

Groovy = Java

+ optional typing

+ closures

+ metaprogramming

+ scripting

+ GDK library

- boilerplate

Java Assert

assertEquals(person.getAccountName(accounNumber), "AC1220");
org.junit.ComparisonFailure: 
Expected :AC[20]0
Actual   :AC[122]0
assert person.getAccountName(accounNumber) == "AC1220"
Caught: Assertion failed: 

assert person.getAccountName(accounNumber) == "AC1220"
       |      |              |             |
       |      AC200          |             |
       |                    100           false
       Person("Ivan", 12)

Groovy Assert

Java SoftAssert

check {
    softly { assert frodo.name == "Frodo" }
    softly { assert frodo.race == "Hobbit" }
}
SoftAssertions softly= new SoftAssertions();
softly.assertThat(frodo.getName()).isEqualTo("Frodo");
softly.assertThat(frodo.getRace()).is("Hobbit");
softly.assertAll();

Groovy SoftAssert

<response version-api="2.0">
        <value>
            <books>
                <book available="20" id="1">
                    <title>Don Xijote</title>
                    <author id="1">Manuel De Cervantes</author>
                </book>
                <book available="14" id="2">
                    <title>Catcher in the Rye</title>
                   <author id="2">JD Salinger</author>
               </book>
               <book available="13" id="3">
                   <title>Alice in Wonderland</title>
                   <author id="3">Lewis Carroll</author>
               </book>
               <book available="5" id="4">
                   <title>Don Xijote</title>
                   <author id="4">Manuel De Cervantes</author>
               </book>
           </books>
       </value>
    </response>

Parse XML

Java way

org.jdom.input.SAXBuilder saxBuilder = new SAXBuilder();
try {
    org.jdom.Document doc = saxBuilder.build(new StringReader(xml));
    String message = doc.getRootElement().getText();
    System.out.println(message);
} catch (JDOMException e) {
    // handle JDOMException
} catch (IOException e) {
    // handle IOException
}


DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = null;
try {
    db = dbf.newDocumentBuilder();
    InputSource is = new InputSource();
    is.setCharacterStream(new StringReader(xml));
    try {
        Document doc = db.parse(is);
        String message = doc.getDocumentElement().getTextContent();
        System.out.println(message);
    } catch (SAXException e) {
        // handle SAXException
    } catch (IOException e) {
        // handle IOException
    }
} catch (ParserConfigurationException e1) {
    // handle ParserConfigurationException
}
def books = new XmlSlurper().parseText(xml)

Groovy XML

<book available="20" id="1">
    <title>Don Xijote</title>
    <author id="1">Manuel De Cervantes</author>
</book>
def title = books.book.find { it.author.@id == 1 }.title

Map Pain

Map<Integer,Person> map = new HashMap<>();
map.put(1, new Person("Ivan", 23));
map.put(2, new Person("Dima", 12));
map.put(3, new Person("Vlad", 34));
map = [1: new Person("Ivan", 23), 2: new Person("Dima", 12), 3: new Person("Vlad", 34)]
Map<Integer, Person> persons =
                map.entrySet()
                        .stream()
                        .filter(m -> m.getValue().getAge() > 18)
                        .collect(Collectors.toMap(m -> m.getKey(), m -> m.getValue()));
map.findAll { it.value.age > 18 }

Filter Map

Groovy way

Properties as a code

browser = 'firefox'

emails = [from: 'director', to: 'accountant']

environments {
    dev {
        host = "devthost"
    }
    test {
        host = "testhost"
        browser = 'ie'
    }
}

Config.groovy

This is CODE

Groovy Configuration

def config = new ConfigSlurper("dev").parse(Config)

assert config.host == "devhost"
assert config.browser == "firefox"
assert config.emails.from == "director"
assert config.emails.to == "accountant"

Flexible and elegant

Groovy Sshoogr

https://github.com/aestasit/sshoogr

@Grab('com.aestasit.infrastructure.sshoogr:sshoogr:0.9.18')
import static com.aestasit.infrastructure.ssh.DefaultSsh.*

remoteSession('user2:654321@localhost:2222') {  
  exec 'rm -rf /tmp/*'
  exec 'touch /var/lock/my.pid'
  remoteFile('/var/my.conf').text = "enabled=true"


  def file = "/root/home/test.txt"
  def selenium = "selenium-server-standalone-2.48.2.jar"

    
  //read from file
  print remoteFile(file).text

  //copy file to remote host
  scp {
      from { localFile "/root/home/${selenium}"}
      into { remoteFile "/root/remote/${selenium}" }
  }  
}

Canonical PageObject

public class MainPage extends PageObject {
    @FindBy(linkText = ".all")
    WebElement allbooksButton;

    @FindBy(linkText = ".search")
    WebElement searchButton;

    @FindBy(name = "query")
    WebElement searchField;

    @FindBy(css = "button")
    WebElement searchBegin;

    public BookPage(WebDriver driver) {
        super(driver);
    }

Groovy PageObject

class MainPage extends Page {

    





}
static url = "http://ukr.net"

static content = {
     loginInput {$ ".loginForm input"}
     passwordInput { $ ".password input"}
     submit {$ ".submit button"}
     error {$ ".error-text"}
}

http://automation-remarks.com/groovy-page/

Groovy Page Component

//DeclareComponent
class LoginForm extends Page {

    static content = {
         loginInput {$ ".loginForm input"}
         passwordInput { $ ".password input"}
         submit {$ ".submit button"}
         error {$ ".error-text"}
    }

    def login(String name,String password){
        loginInput << name
        passwordInput << password
        submit.click()
    }

}

Groovy Page vs Component

class MainPage extends Page {

    @Component
    LoginFrom loginForm
}

Page object become tiny

As a Result

Team efficiency increased twice

We stoped writing boilerplate

Readable and maintainable tests

def "shouldBeLoginError"() { 
  MainPage mainPage = go MainPage
  mainPage.loginForm.login("test","test")
  assert mainPage.loginForm.errorText == "Wrong credentials"
}

But not everything is so good!

  • Runtime bugs
  • Less performant
  • IDE support

Groovy works 10%  slower than Java

But it the same as Python, Ruby, PHP...

Moreover you can easily speed up with @CompileStatic

Use power of Intelij Idea to support dynamics

Be dynamic

Build your test automation with Groovy !

Thank you!

twitter: @s_pirogov

blog: http://automation-remarks.com

[QA Conf] How to make your test automation Groovier

By Sergey Pirogov

[QA Conf] How to make your test automation Groovier

  • 1,315