Search code examples
javaspringgradlegroovyspock

Using Spock framework with Groovy 2.6+ to support Java 8+ syntax


I have a Java library compiled using JDK 9 syntax. But, I am using spock-core:1.0-groovy-2.4, Which does not support JDK 8+ syntax. So, in my Java code, I can use lambdas, default methods, and method references. But, in my tests I cannot. I have to use a clunky workaround for lambdas where I write a Groovy closure, then cast it to a Function<...> using the as keyword. But, other syntax such as method references like Class::method simply will not compile. Below is the dependencies section of my build.gradle.

How can I upgrade it such that I am using a newer version of Groovy that supports newer versions of Java?

All of there documentation seems to be out of date, and only references up to Groovy 2.4.

dependencies {
  testCompile(
    'junit:junit:4.12',
    'org.codehaus.groovy:groovy-all:2.4.4',
    'org.spockframework:spock-core:1.0-groovy-2.4',
    'org.springframework.boot:spring-boot:1.2.1.RELEASE',
    'cglib:cglib-nodep:2.2.2',
    'com.opencsv:opencsv:4.1'
  )
}

Solution

  • Even when working with Spock 1.2-groovy-2.4-SNAPSHOT and Groovy 3.0.0-alpha-1 you cannot directly use Java's lambda and method reference syntax, I just tested it in my sample Spock/Geb Maven project in IntelliJ IDEA. But even with Spock 1.1 and Groovy 2.4.7 it it quite simple and elegant to replace lambdas and method references without casting or as, as far as I saw in my quick test:

    package de.scrum_master.stackoverflow
    
    import groovy.transform.ToString
    import groovy.transform.TupleConstructor
    import spock.lang.Specification
    
    class LambdaMethodRefTest extends Specification {
    
      def "Replace lambdas and method references in Groovy"() {
        given:
        def bookStream = [
          new Book("Fortran", "Fred", 1957, 57.99),
          new Book("Java in 3 Days", "Alice", 2005, 11.99),
          new Book("Java in 4 Days", "Bob", 2005, 22.99),
          new Book("Filter-Map-Reduce with Lambdas", "Claire", 2014, 33.99)
        ].stream()
    
        when:
        def mapReduceResult = bookStream
          .filter { it.year >= 2004 }
          .peek(System.out.&println)
          .map { it.author }
          .map { it.toUpperCase() }
          .peek { System.out.println(it) }
          .reduce("", { s1, s2 -> (s1.isEmpty()) ? s2 : s1 + ", " + s2 })
    
        then:
        mapReduceResult == "ALICE, BOB, CLAIRE"
      }
    
      @TupleConstructor
      @ToString(includeNames = true, includePackage = false)
      static class Book {
        String title, author; int year; double price
      }
    
    }
    

    Please notice the two different ways of calling System.out.println - your choice. ;-)

    Console log:

    LambdaMethodRefTest$Book(title:Java in 3 Days, author:Alice, year:2005, price:11.99)
    ALICE
    LambdaMethodRefTest$Book(title:Java in 4 Days, author:Bob, year:2005, price:22.99)
    BOB
    LambdaMethodRefTest$Book(title:Filter-Map-Reduce with Lambdas, author:Claire, year:2014, price:33.99)
    CLAIRE
    

    What exactly are you missing?


    Update: Somehow my old code using .map(String.&toUpperCase) is not working anymore after I changed some things in my Maven project. I wonder why it ever worked before. So I have updated the sample code.