... Testing: Spock
@Speck
@RunWith(Sputnik)
class PublisherSubscriberSpeck {
def quot;events are received by all subscribersquot;() {
def pub = new Publisher()
def sub1 = Mock(Subscriber)
def sub2 = Mock(Subscriber)
pub.subscribers << sub1 << sub2
when:
pub.send(quot;eventquot;)
then:
1 * sub1.receive(quot;eventquot;)
1 * sub2.receive(quot;eventquot;)
}
}
Testing: EasyB ...
given quot;an invalid zip codequot;, {
invalidzipcode = quot;221o1quot;
}
and quot;given the zipcodevalidator is initializedquot;, {
zipvalidate = new ZipCodeValidator()
}
when quot;validate is invoked with the invalid zip codequot;, {
value = zipvalidate.validate(invalidzipcode)
}
then quot;the validator instance should return falsequot;, {
value.shouldBe false
}
Testing: EasyB ...
before quot;start seleniumquot;, {
given quot;selenium is up and runningquot;, {
// start selenium
}
}
scenario quot;a valid person has been enteredquot;, {
when quot;filling out the person form with a first and last namequot;, {
selenium.open(quot;http://acme.racing.net/greport/personracereport.htmlquot;)
selenium.type(quot;fnamequot;, quot;Britneyquot;)
selenium.type(quot;lnamequot;, quot;Smithquot;)
}
and quot;the submit link has been clickedquot;, {
selenium.click(quot;submitquot;)
}
then quot;the report should have a list of races for that personquot;, {
selenium.waitForPageToLoad(quot;5000quot;)
values = [quot;Mclean 1/2 Marathonquot;, quot;Reston 5Kquot;, quot;Herndon 10Kquot;, quot;Leesburg 10Kquot;]
for(i in 0..<values.size()){
selenium.getText(quot;//table//tr[${(i+3)}]/tdquot;).shouldBeEqualTo values[i]
}
}
}
after quot;stop seleniumquot; , {
then quot;selenium should be shutdownquot;, {
// stop selenium
}
}
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”
quot;...except for the problem of too many
layers of indirection“
For attributions, see
http://en.wikipedia.org/wiki/Inversion_of_control
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
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) }
}
Remember 100% coverage rule ...
Necessary but not sufficient condition
// 100%
Remember 100% coverage rule ...
Necessary but not sufficient condition
// 100%
// Fail
... Remember 100% coverage rule
Necessary but not sufficient condition
Code style: CodeNarc ...
About 50 rules (1 broken?)
... Code style: CodeNarc ...
... Code style: CodeNarc
Code style: IntelliJ
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
... Duplication: Simian ...
... Duplication: Simian ...
... 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
Builds: Groovy from Ant
Need groovy jar on your Ant classpath
<taskdef name=quot;groovyquot;
classname=quot;org.codehaus.groovy.ant.Groovyquot;
classpathref=quot;my.classpathquot;/>
<target name=quot;printXmlFileNamesFromJarquot;>
<zipfileset id=quot;foundquot; src=quot;foobar.jarquot;
includes=quot;**/*.xmlquot;/>
<groovy>
project.references.found.each {
println it.name
}
</groovy>
</target>
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(quot;Helloquot;);
}
}
''')
javac(srcdir:'.', includes:'Temp.java', fork:'true')
java(classpath:'.', classname:'Temp', fork:'true')
echo('Done')
}
// =>
// [javac] Compiling 1 source file
// [java] Hello
// [echo] Done
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 ( )
}
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
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
... Builds: Gradle
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
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'
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
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