Javantura v2 - Making Java web-apps Groovy - Franjo Žilić

Post on 05-Jul-2015

421 views 0 download

description

How can you get more done by doing less? Can Spring Web MVC applications look simpler? How can writing less code mean more? How can using Groovy in JUnit tests speed up writing tests. Making data driven tests cool with Spock. All in existing living Java web applications with sample code in Java and Groovy.

Transcript of Javantura v2 - Making Java web-apps Groovy - Franjo Žilić

MAKING JAVA WEB-APPSGROOVY

GROOVY FOR FUN AND PLEASURE.by Franjo Žilić

TODAYGroovy vs. Java

Spring with Groovy

Testing with Groovy

More testing with Groovy

http://groovy.codehaus.org

Groovy is a dynamic language of JVM which adds features towell known Java syntax and JDK libraries inspired by languages

like Python, Ruby, Smalltalk.

...Makes Java code simpler and easier to read.

Interoperable with Java libraries.

Compiles into Java bytecode.

Let's you get more done by doing less.

Types are optional or checked.

SOME DIFFERENCESdefault importssemicolons are optionalreturn is optional== means .equalsgetters and setters appear on their ownpublic by defaulttruth is not what it wasparentheses are optional * but we like them

REAL TIME SAVERSiteration methodsoperators

null safe dereference - ?.elvis - ?:spread - *.regex - =~ ==~subscript - b[0]

WHY GROOVY IN WEB APP?Thought before syntax.

Easy to learn *if we know Java

Less code - fewer errors.

Less code - more tests.

WHY NOT GRAILS

Rewriting takes time.

Taking small steps.

Whole new ecosystem?

Being forced?

INTEGRATIONAnt

Maven

Gradle

ANTNative groovyc integration

Supports any Groovy version

Uses stubs for joint (Java+Groovy) projects

... uses ant

MAVEN

GMAVENPLUSSupports any Groovy version

Not a native maven-compiler plugin

Poor IDE integration (Eclipse)

Uses stubs...

MAVEN

GROOVY ECLIPSE COMPILERNative maven-compiler-plugin

Good IDE integration (Eclipse, IntelliJ)

Restricts Groovy version

No support for invoke-dynamic

GRADLEUses groovyc ant task, so applies as for ant

Decent IDE integration

SAVING TIME IN PRODUCTION CODELet's say we have a model

... we can't change it

... we get a list of UglyJavaModel objects

... anything can be null

REQUIREMENTSsomeone said:

count with theAllImportantBoolean truetop three sorted by someMonetaryValue descending

JAVALet's just count those they want

final List<UglyJavaModel> list = thirdPartyService.onlyWayToGetData();

int countOfThoseWithBooleanTrue = 0;

for (final UglyJavaModel uglyJavaModel : list) { if (uglyJavaModel.getNested() != null && uglyJavaModel.getNested().getNested() != null && uglyJavaModel.getNested().getNested().getNested() != null && uglyJavaModel.getNested().getNested().getNested() .getTheAllImportantBoolean() == true) {

countOfThoseWithBooleanTrue++; }}model.addAttribute("countWithBooleanTrue", countOfThoseWithBooleanTrue);

Java can do better, but...

JAVAAnd just top three

final List<UglyJavaModel> list = thirdPartyService.onlyWayToGetData();

list.sort(new Comparator<UglyJavaModel>() { @Override public int compare(final UglyJavaModel left, final UglyJavaModel right) { return left.getNested().getSomeMonetaryValue() .compareTo(right.getNested().getSomeMonetaryValue()) * -1; }});

final List<String> topThree = new ArrayList<String>(3);for (final UglyJavaModel uglyJavaModel : list.subList(0, 3)) { topThree.add(uglyJavaModel.getSomeName());}model.addAttribute("topThree", topThree);

GROOVYI know, this is boring

def list = thirdPartyService.onlyWayToGetData()

model.addAttribute("countWithBooleanTrue", list.count { it?.nested?.nested?.nested?.theAllImportantBoolean})

model.addAttribute("topThree", list.sort { a, b -> b.nested.someMonetaryValue <=> a.nested.someMonetaryValue}[0..2]*.someName)

TESTINGJUnit is good enough

... but we still have to write a lot of code

... and we need a lot of libraries

WHY GROOVYsimpler asserts

dynamic groovy

less code

CLASSIC JUNIT4 TEST @RunWith(MockitoJUnitRunner.class) //omitted imports - Mockito, Assertionspublic class TipicalJunitTest { @Test //ommited mocks and prepare data public void testUglyJavaControllerReturingCorrectCount() { final UglyJavaController controller = new UglyJavaController(service); final ArrayList<UglyJavaModel> uglyJavaModels = prepareData(); when(service.onlyWayToGetData()).thenReturn(uglyJavaModels); final String view = controller.showSomething(model); assertThat(view).isEqualTo("ugly"); verify(model).addAttribute(eq("countWithBooleanTrue"), eq(1)); verify(model).addAttribute(eq("topThree"), captor.capture()); verify(service).onlyWayToGetData(); verifyNoMoreInteractions(service, model); assertThat(captor.getValue()).containsExactly("first","third","second"); }}

SAME TEST, GROOVY WAY class GroovyJunitTest { def data = [/* prepare data*/] // use mocks if you want @Test void testUglyJavaControllerReturingCorrectCount() { def controller = new UglyJavaController([onlyWayToGetData: { return data }] as ThirdPartyService) assert controller.showSomething([addAttribute: { String attr, value -> if (attr == 'countWithBooleanTrue') { assert value == 1 } else if (attr == 'topThree') { assert value == ['first', 'third', 'second'] } else { assert false } }] as Model) == 'ugly' }}

DATA DRIVEN TESTS... the dreaded Parameterized runner

What about data driven with Spring context?

No out of the box solution.

WELCOME SPOCK... an expressive testing and specification framework

brings new meaning to data driven

Written in Groovy, inspired by others*, compatible with JUnit*JUnit, jMock, Mockito, RSpec...

@ContextConfiguration(['classpath:spring/test-config.xml'])class SneakPeekSpockSpecification extends Specification { @Autowired SimpleSpringService service

@Unroll void "just a simple sneak peak for spock"() { given: def testService = new SpockDemoService(service) expect: "dependencies are met" service

when: "calculate average for #booleanValue #givenValues.inspect()" def average = testService.averageValueFor(booleanValue, givenValues.collect { return new UglyJavaModel(nested: new UglyJavaModelNested(someMonetaryValue: it.d, nested: new UglyJavaModelNestedNested(nested: new UglyJavaModelNestedNestedNested(theAllImportantBoolean: it.b)))) })

then: "average value should be #expected" average == expected

where: booleanValue|givenValues || expected true |[[b:true,d:42.98],[b:false,d:4.3],[b:true,d:27.31]]||35.14 false |[[b:true,d:42.98],[b:false,d:4.3],[b:true,d:27.31]]||4.3 true |[[b:true,d:42.98],[b:true,d:4.3],[b:true,d:27.31]] ||24.86 }}

https://github.com/fzilic/making-java-groovy

QUESTIONS?