Grails 3.3 Unit Tests (vs Grails 2.4)

I’ve spent some of this weekend migrating an old Grails 2.0 application to Grails 3.3. It went pretty well. I created a new project and then copied domain, controller and service classes over. There is still some work to do, migrating tests and refactoring code to use the asset pipeline.

Here I share what I’ve learned about the changes between unit tests in Grails 2.4 and 3.3.

  1. IntelliJ IDEA 16 doesn’t properly support Grails 3.3 yet
  2. Grails 2.4 and 3.3 on the same machine sometimes collide
  3. Domain/service mocking has changed since 2.4

Intellij IDEA Support

I’ve run in to problems running tests in another project. The tests run just fine from the command line, but not in IDEA. With further reading last night I found that IDEA fails to add “-integration” or “-unit” to the run/debug configuration commands. This results in Grails reporting no tests found. It should look like something like test-app AreaControllerSpec -echoOut -unit.

Once I added this the test ran but be aware that IDEA continues to report “Test framework quit unexpectedly” in all cases. If you ignore this, and perhaps use the very pretty HTML formatted reports, you can make progress.

Grails 2.4/3.3 Conflict

The main project that I’m working on is 2.4. When I tried to run the tests from the command line (as noted above) but they failed to even start, reporting missing dependencies (junit-dep and cglib-nodep).

 ::::::::::::::::::::::::::::::::::::::::::::::
 :: UNRESOLVED DEPENDENCIES ::
 ::::::::::::::::::::::::::::::::::::::::::::::
 :: junit#junit-dep;4.10: not found
 :: cglib#cglib-nodep;2.2.2: not found
 ::::::::::::::::::::::::::::::::::::::::::::::

The found that the tests needed to be run from within IDEA.

Domain/Service Mocking

A key test that I have migrated is a controller integration test in the 2.0 project. I copied the methods over to a unit test and then needed to mock the domain objects and the services that were called by the controller. (The examples on the Grails documentation website didn’t show how to do this; how unrealistic is it to give an example with no reference to services?) Notice the lack of @Mock annotations.

import grails.testing.gorm.DataTest
import grails.testing.web.controllers.ControllerUnitTest
import org.grails.testing.GrailsUnitTest
import spock.lang.Specification

class AreaControllerSpec extends Specification implements
 ControllerUnitTest<AreaController>, DataTest, GrailsUnitTest {
 
def setup() {
    defineBeans {
        areaService(AreaService)
        companyService(CompanyService)
        sessionService(SessionService)
    }
    mockDomains Area, Company
}

...

One useful link is this commit on GitHub. It’s not exactly readable but it gives the changes implemented to arrive at the new testing framework.

 

Flattr this!

flatten()

I post this because, as I continue to learn Goovy/Grails, I keep getting stuck on stupid problems that should be simple.

I needed to count the number of items in a nested collection. I thought that the Groovy spread operator would give me the solution, but it didn’t. At work I just went for the easy option of nested each construction incrementing a counter. Messy, but I really don’t have time to dick around and it gave me a working solution.

So this evening I spent some time on the problem and discovered this, perhaps obvious, easy solution. The unit test below illustrates this.

import spock.lang.*
import grails.test.mixin.*

@Mock([Employee, Company, CompanyGroup])
class CompanyGroupSpec extends Specification {

	void "Test spread"() {
		
		when: "We have a group of companies with employees"
		
		Employee alice = new Employee(employeeName: 'Alice')
		Employee bob = new Employee(employeeName: 'Bob')
		Company acme = new Company(companyName: 'Acme')
		acme.addToEmployees(alice)
		acme.addToEmployees(bob)
		
		Employee claire = new Employee(employeeName: 'Claire')
		Employee dave = new Employee(employeeName: 'Dave')
		Company bravo = new Company(companyName: 'Bravo')
		bravo.addToEmployees(claire)
		bravo.addToEmployees(dave)
		
		CompanyGroup group = new CompanyGroup(groupName: 'Super Group')
		group.addToCompanies(acme)
		group.addToCompanies(bravo)
		
		then: "spread gives unexpected results"
		
		acme.employees.size() == 2
		bravo.employees.size() == 2
		
		group*.companies*.employees.size() == 1
		
		group*.companies*.employees.flatten().size() == 4
		
	}
	
}

As you can see, I want a count of the number of employees in all companies in the group. I expected four. If the map isn’t flattened then I end up with a count of outer container, which is always one.

I hope this snippet has saved you enough time to treat yourself to a coffee :-)

Flattr this!