240 likes | 426 Views
Scripting Approaches For Java Application Test Automation. Marty Kube Beaver Creek Consulting Corp NoVa Test Automation Interest Group 2009-04-08. http://www.novataig.net/. http://beavercreekconsulting.com/. Scripting and Test Automation. Scripting is the way to go for test automation
E N D
Scripting Approaches For Java Application Test Automation Marty Kube Beaver Creek Consulting Corp NoVa Test Automation Interest Group 2009-04-08 http://www.novataig.net/ http://beavercreekconsulting.com/
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
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(inti = 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(inti = 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://www.mozilla.org/rhino/ http://en.wikipedia.org/wiki/Javascript
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() } varsubLedger = 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 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 • >javaccTestLedger.jj • >javac -d bin -cp "../bin" gen\*.java • >java -classpath "bin;../bin" LedgerLanguage TestCase.txt
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