Scripting Approaches For Java Application Test Automation

coordinatedcapableΛογισμικό & κατασκευή λογ/κού

4 Νοε 2013 (πριν από 4 χρόνια και 5 μέρες)

69 εμφανίσεις

MARTY KUBE

BEAVER CREEK CONSULTI NG CORP


NOVA

TEST AUTOMATI ON I NTEREST GROUP

2009
-
04
-
08


Scripting Approaches For Java
Application Test Automation

http://beavercreekconsulting.com/

http://www.novataig.net/

Scripting and Test Automation


Scripting is the way to go for test automation


“High Level” languages
-

More functionality with less code



How scripting interacts with the application


External interfaces


Win runner


JMeter


Expose Internals


JavaScript in the Browser


SQL


Survey a Few DSL Approaches


Small example application


Use scripting and Domain Specific Language to drive
application


Groovy


JavaScript


JavaCC


Hands on with code examples

Contrasting the DSL approaches


Focus on Strategy


Using Java as an example


similar tactics should apply for other environments.


Three types of resources


Expertise for set
-
up, Integration effort, Expertise for test
authoring


Trade offs:


General Trade offs


Less front end setup


More complex test authoring



More front end setup


Less complex test authoring

Scripting Languages


Type
-
less or easy to use type system


High Level


more productive and less defects


Interpreted


Interactive shell for rapid exploration or
development


Text processing
-

AWK, SED


General Purpose
-

Perl, Python, TCL, Emacs LISP

http://en.wikipedia.org/wiki/Scripting

Domain Specific Languages (DSL)


Smaller language dedicated to a particular situation


SQL


Second Life/MORPHS


Spotted in the wild


Less general


Lack low level functionality


More expressive in the particular domain



Really blurry line between scripting language and
DSL

http://en.wikipedia.org/wiki/Domain_specific_language

Example Application



class novataig
«enumerati on»
Account

CHECKING

TRAVEL_AND_ENTERTAINMENT

SLUSH_FUND
«enumerati o...
Ind

DEBIT

CREDIT
JournalEntry
~
account: Account {readOnl y}
~
amount: Doubl e {readOnl y}
~
i nd: Ind {readOnl y}
+
Journal Entry(Account, Ind, Doubl e)
Ledger
~
bal ances: Map<Account, Doubl e> = new HashMap<Acc...
+
getBal ance(Account) : Doubl e
+
post(Journal Entry) : voi d
+
setBal ance(Account, Doubl e) : voi d
~i nd
~account
The Java Code

package
com.beavercreekconsulting.novataig
;

public class
TestLedger

{


public static void main(String[]
args
) {




Ledger
subLedger

= new Ledger();




// Open the period



subLedger.setBalance
(
Account.CHECKING
, 1000.00);



subLedger.setBalance
(
Account.TRAVEL_AND_ENTERTAINMENT
, 200.00);



subLedger.setBalance
(
Account.SLUSH_FUND
,
-
500.00);




// Post activity



JournalEntry

[] activity = {





new
JournalEntry
(
Account.CHECKING
,
Ind.CREDIT
, 100.00),





new
JournalEntry
(
Account.TRAVEL_AND_ENTERTAINMENT
,
Ind.DEBIT
, 100.00),






new
JournalEntry
(
Account.CHECKING
,
Ind.CREDIT
, 2000.00),





new
JournalEntry
(
Account.SLUSH_FUND
,
Ind.DEBIT
, 2000.00)};




for(
int

i

= 0;
i

<
activity.length
;
i
++) subLedger.post(activity[
i
]);






// Check ending balances



if (
subLedger.getBalance
(
Account.CHECKING
) !=
-
1100.00) throw new
TestFailure
();



if (
subLedger.getBalance
(
Account.TRAVEL_AND_ENTERTAINMENT
) != 300.00) throw new
TestFailure
();



if (
subLedger.getBalance
(
Account.SLUSH_FUND
) != 1500.00) throw new
TestFailure
();




System.out.println
("Happy Day!");


}

}

The Groovy Code

package
com.beavercreekconsulting.novataig
;

public class
TestLedger

{


public static void main(String[]
args
) {




Ledger
subLedger

= new Ledger();




// Open the period



subLedger.setBalance
(
Account.CHECKING
, 1000.00);



subLedger.setBalance
(
Account.TRAVEL_AND_ENTERTAINMENT
, 200.00);



subLedger.setBalance
(
Account.SLUSH_FUND
,
-
500.00);




// Post activity



JournalEntry

[] activity = [





new
JournalEntry
(
Account.CHECKING
,
Ind.CREDIT
, 100.00),





new
JournalEntry
(
Account.TRAVEL_AND_ENTERTAINMENT
,
Ind.DEBIT
, 100.00),






new
JournalEntry
(
Account.CHECKING
,
Ind.CREDIT
, 2000.00),





new
JournalEntry
(
Account.SLUSH_FUND
,
Ind.DEBIT
, 2000.00)];




for(
int

i

= 0;
i

<
activity.length
;
i
++) subLedger.post(activity[
i
]);






// Check ending balances



if (
subLedger.getBalance
(
Account.CHECKING
) !=
-
1100.00) throw new
TestFailure
();



if (
subLedger.getBalance
(
Account.TRAVEL_AND_ENTERTAINMENT
) != 300.00) throw new
TestFailure
();



if (
subLedger.getBalance
(
Account.SLUSH_FUND
) != 1500.00) throw new
TestFailure
();




System.out.println
("Happy Day!");


}

}


Groovy Up Close


Super set of Java


Plus


Dynamic typing


Enhanced API Java Classes


String interpolation


def x = “Bob”; print “Hello ${x}”;


Closures


square = {it * it}


[1..9].collect(square);


XML builders and groovy SQL


Script MS Office with COM Bridge (Scriptom)


http://groovy.codehaus.org/Tutorial+2+
-
+Code+as+data,+or+closures

Groovy Architecture


What’s in the JVM

>groovy.bat
-
classpath bin TestLedger.groovy

Really Groovy Code

import com.beavercreekconsulting.novataig.*;


def subLedger = new Ledger();


[ [Account.CHECKING, 1000.00],


[Account.TRAVEL_AND_ENTERTAINMENT, 200.00],


[Account.SLUSH_FUND,
-
500.00]




].each() {subLedger.setBalance(it[0], it[1]);}


[

new JournalEntry(Account.CHECKING, Ind.CREDIT, 100.00),


new JournalEntry(Account.TRAVEL_AND_ENTERTAINMENT, Ind.DEBIT, 100.00),


new JournalEntry(Account.CHECKING, Ind.CREDIT, 2000.00),


new JournalEntry(Account.SLUSH_FUND, Ind.DEBIT, 2000.00)






].each() {je
-
> subLedger.post(je);}


[

[Account.CHECKING,
-
1100.00],


[Account.TRAVEL_AND_ENTERTAINMENT, 300.00],


[Account.SLUSH_FUND, 1500.00]


].each() { expected
-
>


if(subLedger.getBalance(expected[0]) != expected[1]) throw new TestFailure();

}

println "Happy Day!"

JavaScript


JavaScript is a small language


ECMA Script


Easy to integrate


Easy to find JavaScript Fan boys


Rhino is a JavaScript interpreter written in Java


Other JavaScript Engines, Mozilla, V8



http://en.wikipedia.org/wiki/Javascript

http://www.mozilla.org/rhino/

Rhino Architecture

>java
-
classpath lib
\
js.jar;bin
org.mozilla.javascript.tools.shell.Main


-
f TestLedger.js

The JavaScript Code

importPackage
(
com.beavercreekconsulting.novataig
)


function
assertEquals
(a, b) {


if(a != b) throw new
TestFailure
()

}


var

subLedger

= new Ledger()


subLedger.setBalance
(
Account.CHECKING
, 1000.00)

subLedger.setBalance
(
Account.TRAVEL_AND_ENTERTAINMENT
, 200.00)

subLedger.setBalance
(
Account.SLUSH_FUND
,
-
500.00)


activity = [


new
JournalEntry
(
Account.CHECKING
,
Ind.CREDIT
, 100.00),


new
JournalEntry
(
Account.TRAVEL_AND_ENTERTAINMENT
,
Ind.DEBIT
, 100.00),



new
JournalEntry
(
Account.CHECKING
,
Ind.CREDIT
, 2000.00),


new
JournalEntry
(
Account.SLUSH_FUND
,
Ind.DEBIT
, 2000.00)]


for(
i

= 0;
i

<
activity.length
;
i
++) subLedger.post(activity[
i
])


assertEquals
(
subLedger.getBalance
(
Account.CHECKING
),
-
1100.00)

assertEquals
(
subLedger.getBalance
(
Account.TRAVEL_AND_ENTERTAINMENT
), 300.00)

assertEquals
(
subLedger.getBalance
(
Account.SLUSH_FUND
), 1500.00)


print("Happy Day!")


JavaCC


Create a DSL

#

# Test case: #16,678

# Author: Marty

#


Checking



set 1000.00;

travel_and_entertainment


set 200.00;

slush_fund



set
-
500.00;




checking



post credit 100.00;

travel_and_entertainment


post debit 100.00;

checking



post credit 2000.00;

slush_fund



post debit 2000.00;






Checking



verify
-
1100.00;

travel_and_entertainment


verify 300.00;

slush_fund




verify 1500.00;


print happy day!;

https://javacc.dev.java.net/

JavaCC

JavaCC Lexer

SKIP: {" "}


TOKEN: { < CHECKING: "checking" >}

TOKEN: { < OP_SET : "set" > }

TOKEN: { < NUMBER : (["0"
-
"9"])+ > }

TOKEN: { < EOL: ";" >}


<CHECKING> <OP_SET> <NUMBER> <EOL>

Checking set 1000.00;



JavaCC Parser

Accepts the token stream
:

<CHECKING> <OP_SET> <NUMBER> <EOL>

<TRAVEL> <OP_POST> <NUMBER> <EOL>

<EOF>


And enforces order


the grammar
:

(<CHECKING> <OP_SET> <NUMBER> <EOL> )*


<EOF>



Recognize Statements


Invoke Application

{(


<CHECKING>


<OP_SET>


t=<NUMBER>


{ amount = new Double(
t.image
); }


<EOL>


{
System.out.println
("Posting: " + amount); }


{
ledger.setBalance
(
Account.CHECKING
, amount);

})*


<EOF>


{
System.out.println
("Happy Day");}

}



Running


>
javacc

TestLedger.jj


>
javac

-
d bin
-
cp "../bin" gen
\
*.java


>
java

-
classpath "bin;../bin" LedgerLanguage
TestCase.txt

Comparison
-

LOE

Test
Harness
Authoring

Integration

Test
Authoring

Groovy

H

L

H

JavaScript

L

L

M

JavaCC

H

H

L

Honorable mentions


SWIG


Antlr


JavaScript


Mozilla


v8


Most scripting languages cross language interfaces,
Perl, Python, etc.


Bean Shell


Jython




Wrap up


JVM is a target for many scripting languages


Using a JVM based language make integration easy


DSL can make it easy to author test cases


Setup can be a hurdle