430 likes | 596 Views
Test-Driven in Groovy. Joseph Muraski Christopher Bartling. Joseph Muraski. Independent Consultant in the Twin Cities Develop enterprise applications using Java, .Net , Groovy, Grails… Email: joe.muraski@mantacs.com Twitter: jmuraski Blog: joemuraski.blogspot.com.
E N D
Test-Driven in Groovy Joseph Muraski Christopher Bartling
Joseph Muraski • Independent Consultant in the Twin Cities • Develop enterprise applications using Java, .Net, Groovy, Grails… • Email: joe.muraski@mantacs.com • Twitter: jmuraski • Blog: joemuraski.blogspot.com
Christopher Bartling • Independent consultant, based in the Twin Cities • Teach, mentor and coach for DevJam • Experiences include building enterprise applications with Java, Groovy, Grails, .NET, and Adobe Flex • Email: chris.bartling@gmail.com • Twitter: cbartling • Blog: bartling.blogspot.com
Goals of the workshop • Introduce features in Groovy that will help your Java testing efforts • Introduce Cucumber and cuke4duke as new tools in your testing arsenal • Demonstrate unit, integration, and acceptance testing with Groovy and Cucumber (via cuke4duke) via an example Java web application • Get hands on with these technologies
Summary • Design principles • Testing facilities built into Groovy • Acceptance/BDD testing with Groovy • Cucumber and cuke4duke • Presentation and code available • http://bitbucket.org/joe.muraski/grails_tdd_workshop/ • Instructions on how to use Mercurial to clone the repository or retrieve everything in an archive file
Prerequisites for hands on labs • Java 1.6 JDK installation • Maven 2 • That’s it…Maven will take care of resolving all the other dependencies
Maven 2 • Maven2 installation required • We’re using version 2.2.1 • This is not a Maven presentation • We use it because it’s quite handy and does a great job of resolving dependencies • Maven commands will be presented when needed
Maven plugins used • org.codehaus.gmaven:gmaven-plugin:1.2 • org.mortbay.jetty:maven-jetty-plugin • cuke4duke:cuke4duke-maven-plugin:0.2.4 • org.apache.maven.plugins:maven-surefire-plugin
Sample web application • Starting the web app • mvn jetty:run-exploded • Running tests • mvn tests • Running cuke4duke • mvn cuke4duke:cucumber
Design principles • Single responsibility per class (high cohesion) • Loose coupling of collaborators (low coupling) • Injection of dependencies
Why Groovy? • Groovy works seamlessly with Java • It’s all just bytecode in the end • Groovy offers a relaxed Java syntax • Interesting tools included in Groovy • GSQL • XmlSlurper, XmlParser, Builders • GPath • Convenient collections • Private method testing
GroovyTestCase • Included in Groovy distribution • Extends junit.framework.TestCase • Provides a number of helper methods for assertions • Does not require test* methods to be void return type • No need to declared throws checked exceptions in tests • Groovy converts checked exceptions to unchecked exceptions
Convenience assert methods • assertArrayEquals(Object[] expected, Object[] actual) • assertContains(char expected, char[] actualArray) • assertContains(int expected, int[] actualArray) • assertInspect(Object value, String expected) • assertLength(int length, Object[] array) • assertScript(String script) • assertToString(Object value, String expected)
shouldFail() closures • shouldFail(Closure closure) • Asserts supplied closure fails when evaluated • shouldFail(Class desiredThrownType, Closure closure) • Asserts supplied closure fails when evaluated and particular exception type is thrown • shouldFailWithCause(Class desiredCauseType, Closure closure) • Will check for a checked exception as the root cause (checked exceptions are wrapped in Groovy)
Mocking Java classes with Groovy • Map coercion • Groovy’s mock library • GMock library • Java mocking frameworks
Map coercion • Add named closures to the map • Only one closure can be mapped to a “method name” • Cannot overload methods • Coerce to desire type using the as operator
Private Method Calling • Groovy can call private methods • Broken encapsulation? • Better reflection abstractions? • Java can invoke private methods, it’s just more painful • Is testing private methods Good or a Smell?
Groovy mocking library • Groovy's built-in mock support does not allow us to test Java classes • Relies on hooking into Groovy's object lifecycle mechanisms • Java can not make call to Groovy Mock or Stub • Use Java mocking frameworks instead • Can cheat by putting Groovy Mock in a coerced map but why??
GMock • Groovy-based mock objects framework • Easy syntax and usage • Works when called by Java classes
GMock • Create Mock • AddressService service = mock(AddressService) • Create Expectation • service.fetch(“id”).returns(new Address()).once() • Easy Mathcing • service.save(match{it.personId == “id”}).returns(“id”).once()
Java Mocking Frameworks • Java Mocking frameworks can be used with Groovy • Some have minor syntax issues or needed work arounds (JMock) • Great to use if you already have one you are using and don’t want to switch
GSQL • Easy to create connections • con = Sql.newInstance("jdbc:hsqldb:hsql://localhost/", "sa", "", "org.hsqldb.jdbcDriver") • Simple to work with row sets • con.rows(“select * from address”).each {println “id: ${it.id}”} • No try catch blocks
Acceptance Test-Driven • Story tests manifest themselves as executable tests • Drives the development of complete features • Frameworks are available • Fit, FitLibrary, FitNesse • http://fit.c2.com/ • Robot Framework • http://code.google.com/p/robotframework/ • Cucumber • http://cukes.info/ • Others
Cucumber • Tool for executing plain-text functional descriptions as automated tests • Supports BDD • Cucumber tests are typically written before anything else • verified by business analysts, domain experts, non-technical stakeholders • The production code is then written to make the Cucumber tests pass • Outside-in development
Cucumber (via cuke4duke) • Cucumber for the JVM • Outside-in testing • Facilitates automation of acceptance/story tests • Features and scenarios use normal language • Step definitions can be written in Groovy, as we’ll see • Uses JRuby to run the core Cucumber framework • http://wiki.github.com/aslakhellesoy/cuke4duke/
Cucumber features • Purposes • Documentation of the system • Automated, executable acceptance tests • What code should I write next? • Written in Gherkin • Business readable DSL • Describe software behavior without detailing implementation • Grammar exists in different spoken languages (37 currently) • Feature source files have .feature extension
Given-When-Then • Scenarios consist of steps • Given: Put the system in a known state before the user starts interacting with the system; pre-conditions • When: Describe the key action a user performs in this scenario • Then: Observe outcomes; observations should relate to business value of the feature • Use And and But to keep features fluent
Cucumber step definitions • Written in Groovy for our examples • Can be written in many different programming languages • Analogous to method or function definition • Start with adjective/adverb • Regular expression which will match some text from a feature(s) • Take 0 or more arguments • Arguments bound to regular expression groups • Multi-line step arguments
Hooks • Before • Executes before the first step of each scenario • After • Executes after the last step of each scenario • Execute regardless if there are failing, undefined, pending or skipped steps • AfterStep • Executes after each step in a scenario
Hooks • Found in Groovy files in the support directory • Our example uses one: env.groovy • Hooks allow us to run blocks of code at various points in the Cucumber test cycle • No association between where the hook is defined and which scenario/step it is run for • All defined hooks (one or more) are run whenever the relevant event occurs
Tagged hooks • Need to execute a certain hook for only certain scenarios • Achieved by associating a Before, After or AfterStep hook with one or more tags • See the env.groovy file • It uses a Before hook with a @database tag to load the database via DBUnit
Tagging scenarios • Allows you to group scenarios for inclusion or exclusion in a Cucumber run • @wip is provided out of the box • Using tags in cuke4duke and Maven… • mvn cuke4duke:cucumber –DcukeArgs=“--tags @wip” • mvn cuke4duke:cucumber -DcukeArgs=“--tags ~@wip” • Inside of the Maven POM: <cucumberArg>${cukeArgs}</cucumberArg>
Running cuke4duke • The cuke4duke Maven plugin • mvn cuke4duke:cucumber • First time: use –Dcucumber.installGems=true • Add –o option to work offline • Features and step definitions are discovered by the plugin • Features belong in features directory • Step definitions found in the step_definitions directory
cuke4duke acceptance tests • Step definitions will be written in Groovy • Other JVM languages are allowed • Use cuke4duke.GroovyDsl • Use WebDriver: http://code.google.com/p/selenium • Test web UI • Use DbUnit: http://www.dbunit.org/ • Bulk load the database with known fixture data
Workshop activities • Now it’s your turn! • Try your hand at unit testing with Groovy • Refactor the existing web app • Introduce DAO for database functionality • Add services to orchestrate business logic • Write some Cucumber features and build out or reuse the Groovy step definitions • Try to add new features and practice outside-in development • Is ATDD beneficial? Why or why not?
Interesting resources • http://cukes.info/ • http://blog.dannorth.net/whats-in-a-story/ • http://blog.dannorth.net/introducing-bdd/ • http://www.ibm.com/developerworks/java/library/j-pg11094/ • http://wiki.github.com/aslakhellesoy/cucumber/