Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King

Loading...

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

0 comments

Post a comment

    Post a comment
    Embed Video
    Edit your comment Cancel

    8 Favorites

    Industrial Strength Groovy - Tools for the Professional Groovy Developer: Paul King - Presentation Transcript

    1. Industrial Strength Groovy Tools for the Professional Groovy Developer Dr Paul King ASERT Brisbane, Australia
    2. Topics  Testing/Mocking: JUnit, TestNG, EasyB, Spock, Instinct, MockFor, Gmock, EasyMock  Injection: Spring, Guice  Coverage: Cobertura  Code style: CodeNarc, IntelliJ  Duplication: Simian  Documentation: GroovyDoc  Builds: Ant, Gant, GMaven, Gradle, Hudson  Modularisation: Grapes, OSGi
    3. Groovy’s Appeal  Innovators/Thought leaders  Ideas, power, flexibility, novelty, thinking community  Early adopters  Productivity benefits and collegiate community  Leverage JVM and potential for mainstream  Mainstream  Leverage existing Java skills, low learning curve  Leverage JVM and production infrastructure  Professional community  Tools, tools, tools
    4. Testing: JUnit import org.junit.Test import static org.junit.Assert.assertEquals class ArithmeticTest { @Test void additionIsWorking() { assertEquals 4, 2+2 } @Test(expected=ArithmeticException) void divideByZero() { println 1/0 } }
    5. Testing: TestNG import ... public class SimpleTest { @BeforeClass public void setUp() { // code invoked when test is created } @Test(groups = [ \"fast\" ]) public void aFastTest() { System.out.println(\"Fast test\"); } @Test(groups = [ \"slow\" ]) public void aSlowTest() { System.out.println(\"Slow test\"); } }
    6. Mocking: EasyMock import org.easymock.EasyMock mockControl = EasyMock.createStrictControl() mockReverser = mockControl.createMock(Reverser.class) storer = new JavaStorer(mockReverser) testStorage() def testStorage() { expectReverse(123.456, -123.456) expectReverse('hello', 'olleh') mockControl.replay() checkReverse(123.456, -123.456) checkReverse('hello', 'olleh') mockControl.verify() } def expectReverse(input, output) { EasyMock.expect(mockReverser.reverse(input)).andReturn(output) } def checkReverse(value, reverseValue) { storer.put(value) assert value == storer.get() assert reverseValue == storer.getReverse() }
    7. Testing: Instinct class a_default_storer { def storer @initially void create_new_storer() { storer = new Storer() } private check_persist_and_reverse(value, expectedReverse) { storer.put(value) def persisted = storer.get() assert persisted == value def reversed = storer.reverse assert reversed == expectedReverse } @spec def should_reverse_numbers() { check_persist_and_reverse 123.456, -123.456 } @spec def should_reverse_strings() { check_persist_and_reverse 'hello', 'olleh' } @spec def should_reverse_lists() { check_persist_and_reverse([1, 3, 5], [5, 3, 1]) } } check_specs_for a_default_storer
    8. Mocking: MockFor import groovy.mock.interceptor.MockFor def mocker = new MockFor(Collaborator.class) // create the Mock support mocker.demand.one(1..2) { 1 } // demand the 'one' method one // or two times, returning 1 mocker.demand.two() { 2 } // demand the 'two' method // exactly once, returning 2 mocker.use { // start using the Mock def caller = new Caller() // caller will call Collaborator assertEquals 1, caller.collaborateOne() // will call Collaborator.one assertEquals 1, caller.collaborateOne() // will call Collaborator.one assertEquals 2, caller.collaborateTwo() // will call Collaborator.two } // implicit verify here import groovy.mock.interceptor.MockFor def mocker = new MockFor(Collaborator.class) mocker.demand.one(1..2) { 1 } mocker.demand.two() { 2 } mocker.use { def caller = new Caller() assertEquals 1, caller.collaborateOne() assertEquals 1, caller.collaborateOne() assertEquals 2, caller.collaborateTwo() }
    9. Mocking: Gmock ...  Method mocking: mockLoader.load(\"fruit\").returns(\"apple\")  Exception mocking: mockLoader.load(\"unknown\").raises(new RuntimeException())  Stub mocking: mockLoader.load(\"fruit\").returns(\"apple\").stub()  Static method mocking: mockMath.static.random().returns(0.5)  Property mocking: mockLoader.name.returns(\"loader\")  Constructor mocking: def mockFile = mock(File, constructor('/a/path/file.txt'))  Partial mocking: mock(controller).params.returns([id: 3])  Times expectation: mockLoader.load(\"fruit\").returns(\"apple\").atLeastOnce()  Custom matcher: mockLoader.load(match{ it.startsWith(\"fru\") })  Strict ordering: ordered { ... }  Optional support for Hamcrest matcher: mockLoader.put(\"test\", is(not(lessThan(5))))  GMockController if you can't extend GMockTestCase in your test
    10. Mocking: Gmock import org.gmock.GMockTestCase class LoaderTest extends GMockTestCase { void testLoader(){ def mockLoader = mock() mockLoader.load('key').returns('value') play { assertEquals \"value\", mockLoader.load('key') } } }
    11. Testing: Spock ...
    12. ... Testing: Spock @Speck @RunWith(Sputnik) class PublisherSubscriberSpeck { def \"events are received by all subscribers\"() { def pub = new Publisher() def sub1 = Mock(Subscriber) def sub2 = Mock(Subscriber) pub.subscribers << sub1 << sub2 when: pub.send(\"event\") then: 1 * sub1.receive(\"event\") 1 * sub2.receive(\"event\") } }
    13. Testing: EasyB ... given \"an invalid zip code\", { invalidzipcode = \"221o1\" } and \"given the zipcodevalidator is initialized\", { zipvalidate = new ZipCodeValidator() } when \"validate is invoked with the invalid zip code\", { value = zipvalidate.validate(invalidzipcode) } then \"the validator instance should return false\", { value.shouldBe false }
    14. Testing: EasyB ... before \"start selenium\", { given \"selenium is up and running\", { // start selenium } } scenario \"a valid person has been entered\", { when \"filling out the person form with a first and last name\", { selenium.open(\"http://acme.racing.net/greport/personracereport.html\") selenium.type(\"fname\", \"Britney\") selenium.type(\"lname\", \"Smith\") } and \"the submit link has been clicked\", { selenium.click(\"submit\") } then \"the report should have a list of races for that person\", { selenium.waitForPageToLoad(\"5000\") values = [\"Mclean 1/2 Marathon\", \"Reston 5K\", \"Herndon 10K\", \"Leesburg 10K\"] for(i in 0..<values.size()){ selenium.getText(\"//table//tr[${(i+3)}]/td\").shouldBeEqualTo values[i] } } } after \"stop selenium\" , { then \"selenium should be shutdown\", { // stop selenium } }
    15. Dependency Injection  Hollywood Principle  Don’t call us, we’ll call you  “All problems in computer science can be solved by another level of indirection”  \"...except for the problem of too many layers of indirection“  For attributions, see http://en.wikipedia.org/wiki/Inversion_of_control
    16. Dependency Injection  Pattern for loosely coupled & testable objects class Client { class Client { Calculator calc = Calculator calc new CalculatorImpl() def executeCalc(a, b) { def executeCalc(a, b) { calc.add(a, b) calc.add(a, b) } } } }  Need to select setter,  Service locator/factory constructor, field style  Tightly coupled?  Can add complexity  Hard to test?  Manage configuration  Easy to understand?  Direct or framework  Refactoring/navigation?  Consistency/lifecycle
    17. Dependency Injection: Spring ...  Several flavors  let’s look at Annotation and BeanBuilder flavors import org.springframework.stereotype.Component @Component class AdderImpl { def add(x, y) { x + y } } import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component @Component class CalcImpl3 { @Autowired private AdderImpl adder def doAdd(x, y) { adder.add(x, y) } }
    18. ... Dependency Injection: Spring import org.springframework.context.support.GenericApplicationContext import org.springframework.context.annotation.ClassPathBeanDefinitio nScanner def ctx = new GenericApplicationContext() new ClassPathBeanDefinitionScanner(ctx).scan('') ctx.refresh() def calc = ctx.getBean('calcImpl3') println calc.doAdd(3, 4) // => 7 def bb = new grails.spring.BeanBuilder() bb.beans { adder(AdderImpl) calcBean(CalcImpl2) { delegate.adder = adder } } def ctx = bb.createApplicationContext() def calc = ctx.getBean('calcBean') println calc.doAdd(3, 4) // => 7
    19. Dependency Injection: Guice import com.google.inject.* @ImplementedBy(CalculatorImpl) interface Calculator { def add(a, b) } @Singleton class CalculatorImpl implements Calculator { private total = 0 def add(a, b) { total++; a + b } def getTotalCalculations() { 'Total Calculations: ' + total } String toString() { 'Calc: ' + hashCode()} } class Client { @Inject Calculator calc // ... } def injector = Guice.createInjector() // ...
    20. Dependency Injection: Metaprogramming Style class Calculator { def total = 0 def add(a, b) { total++; a + b } } def INSTANCE = new Calculator() Calculator.metaClass.constructor = { -> INSTANCE } def c1 = new Calculator() def c2 = new Calculator() assert c1.add(1, 2) == 3 assert c2.add(3, 4) == 7 assert c1.is(c2) assert [c1, c2].total == [2, 2]
    21. Coverage: Cobertura ... Ant
    22. ... Coverage: Cobertura ... Maven cobertura-instrument java –cp ... Command-line cobertura-report cobertura-check cobertura-merge Grails grails install-plugin code-coverage --global
    23. ... Coverage: Cobertura
    24. Remember 100% coverage rule ...  Necessary but not sufficient condition // 100%
    25. Remember 100% coverage rule ...  Necessary but not sufficient condition // 100% // Fail
    26. ... Remember 100% coverage rule  Necessary but not sufficient condition
    27. Code style: CodeNarc ... About 50 rules (1 broken?)
    28. ... Code style: CodeNarc ...
    29. ... Code style: CodeNarc
    30. Code style: IntelliJ
    31. Duplication: Simian ... Simian fully supports the following languages:  Java  C#  C++ with partial support for:  C • JSP  Objective-C • ASP  JavaScript (ECMAScript) • HTML  COBOL, ABAP • XML  Ruby  Lisp  SQL  Visual Basic  Groovy
    32. ... Duplication: Simian ...
    33. ... Duplication: Simian ...
    34. ... Duplication: Simian Similarity Analyser 2.2.23 - http://www.redhillconsulting.com.au/products/simian/index.html Copyright (c) 2003-08 RedHill Consulting Pty. Ltd. All rights reserved. Simian is not free unless used solely for non-commercial or evaluation purposes. {failOnDuplication=true, ignoreCharacterCase=true, ignoreCurlyBraces=true, ignoreIdentifierCase=true, ignoreModifiers=true, ignoreStringCase=true, threshold=6} Found 6 duplicate lines in the following files: Between lines 201 and 207 in /Users/haruki_zaemon/Projects/redhill/simian/build/dist/src/java/awt/image/W ritableRaster.java ... Found 66375 duplicate lines in 5949 blocks in 1260 files Processed a total of 390309 significant (1196065 raw) lines in 4242 files Processing time: 9.490sec
    35. Documentation: GroovyDoc ... <taskdef name=\"groovydoc\" classname=\"org.codehaus.groovy.ant.Groovydoc\"> <classpath> <path path=\"${mainClassesDirectory}\" /> <path refid=\"compilePath\" /> </classpath> </taskdef> <groovydoc destdir=\"${docsDirectory}/gapi\" sourcepath=\"${mainSourceDirectory}\" packagenames=\"**.*\" use=\"true\" windowtitle=\"${title} \" doctitle=\"${title}\" header=\"${title}\" footer=\"${docFooter}\" overview=\"src/main/overview.html\" private=\"false\"> <link packages=\"java.,org.xml.,javax.,org.xml.\" href=\"http://java.sun.com/j2se/1.5.0/docs/api\" /> <link packages=\"org.apache.ant.,org.apache.tools.ant.\" href=\"http://www.dpml.net/api/ant/1.7.0\" /> <link packages=\"org.junit.,junit.framework.\" href=\"http://junit.sourceforge.net/junit3.8.1/javadoc/\" /> </groovydoc>
    36. ... Documentation: GroovyDoc
    37. Builds: Groovy from Ant  Need groovy jar on your Ant classpath <taskdef name=\"groovy\" classname=\"org.codehaus.groovy.ant.Groovy\" classpathref=\"my.classpath\"/> <target name=\"printXmlFileNamesFromJar\"> <zipfileset id=\"found\" src=\"foobar.jar\" includes=\"**/*.xml\"/> <groovy> project.references.found.each { println it.name } </groovy> </target>
    38. Builds: Ant from Groovy  Built-in (need ant.jar on your Groovy classpath) new AntBuilder().with { echo(file:'Temp.java', ''' class Temp { public static void main(String[] args) { System.out.println(\"Hello\"); } } ''') javac(srcdir:'.', includes:'Temp.java', fork:'true') java(classpath:'.', classname:'Temp', fork:'true') echo('Done') } // => // [javac] Compiling 1 source file // [java] Hello // [echo] Done
    39. Builds: Gant  lightweight façade on Groovy's AntBuilder  target def’ns, pre-defined ‘ant’, operations on predefined objects includeTargets << gant.targets.Clean cleanPattern << [ '**/*~' , '**/*.bak' ] cleanDirectory << 'build' target ( stuff : 'A target to do some stuff.' ) { println ( 'Stuff' ) depends ( clean ) echo ( message : 'A default message from Ant.' ) otherStuff ( ) } target ( otherStuff : 'A target to do some other stuff' ) { println ( 'OtherStuff' ) echo ( message : 'Another message from Ant.' ) clean ( ) }
    40. Builds: GMaven  Implementing Maven plugins has never been Groovier!  Groovy Mojos  A Simple Groovy Mojo  Building Plugins  Project Definition  Mojo Parameters  Putting More Groove into your Mojo  Using ant, Using fail()  gmaven-archetype-mojo Archetype  gmaven-plugin Packaging
    41. Builds: GMaven <plugin> <groupId>org.codehaus.mojo.groovy</groupId> <artifactId>groovy-maven-plugin</artifactId> <executions> <execution> <id>restart-weblogic</id> <phase>pre-integration-test</phase> <goals> <goal>execute</goal> </goals> <configuration> <source> ${pom.basedir}/src/main/script/restartWeblogic.groovy </source> </configuration> </execution> ...
    42. Builds: GMaven def domainDir = project.properties['weblogic.domain.easyimage.dir'] stopWebLogic() copyFiles(domainDir) startWebLogic(domainDir) waitForWebLogicStartup() def stopWebLogic() { weblogicServerDir = project.properties['weblogic.server.dir'] adminUrl = project.properties['easyimage.weblogic.admin.t3'] userName = 'weblogic' password = 'weblogic' ant.exec(executable: 'cmd', failonerror: 'false') { arg(line: \"/C ${wlsDir}/bin/setWLSEnv.cmd && java ...\" ... } ...
    43. Builds: Gradle ...  A very flexible general purpose build tool like Ant  Switchable, build-by-convention frameworks a la Maven. But we never lock you in!  Very powerful support for multi-project builds  Very powerful dependency management (based on Apache Ivy)  Full support for your existing Maven or Ivy repository infrastructure  Support for transitive dependency management without the need for remote repositories or pom.xml and ivy.xml files  Ant tasks as first class citizens  Groovy build scripts  A rich domain model for describing your build
    44. ... Builds: Gradle
    45. Builds: Hudson  Gant Plugin — This plugin allows Hudson to invoke Gant build script as the main build step  Gradle Plugin — This plugin allows Hudson to invoke Gradle build script as the main build step  Grails Plugin — This plugin allows Hudson to invoke Grails tasks as build steps  Hudson CLI and GroovyShell Usage pattern? Source: http://weblogs.java.net/blog/kohsuke/archive/2009/05/hudson_cli_and.html
    46. Modularisation: Grapes // Google Collections example import com.google.common.collect.HashBiMap @Grab(group='com.google.collections', module='google-collections', version='1.0-rc1') def getFruit() { [ grape:'purple', lemon:'yellow', orange:'orange' ] as HashBiMap } assert fruit.lemon == 'yellow' assert fruit.inverse().yellow == 'lemon'
    47. Modularisation: OSGi This is Apache Sling in five bullets:  REST based web framework  Content-driven, using a JCR content repository  Powered by OSGi  Scripting inside, multiple languages  Apache Open Source project See also: Grails JCR plugin See also: http://hamletdarcy.blogspot.com
    48. Modularisation: Futures  Future ... possibly maybe ... Grapes then OSGi  Command line/Conf file grab support (thanks Danno)  Grab within a script without class/method  Grapes may have some light-weight hooks  groovyMethods  groovyStaticMethods  groovyExpandoMethods  groovyAstTransforms  groovyBuilderMetadata  Auto imports  Runner registration

    + Paul KingPaul King, 6 months ago

    custom

    2148 views, 8 favs, 0 embeds more stats

    More info about this document

    CC Attribution License

    Go to text version

    • Total Views 2148
      • 2148 on SlideShare
      • 0 from embeds
    • Comments 0
    • Favorites 8
    • Downloads 53
    Most viewed embeds

    more

    All embeds

    less

    Flagged as inappropriate Flag as inappropriate
    Flag as inappropriate

    Select your reason for flagging this presentation as inappropriate. If needed, use the feedback form to let us know more details.

    Cancel
    File a copyright complaint
    Having problems? Go to our helpdesk?

    Categories