mockingspockstubbinggrails-2.2

Grails 2.2.4/Spock: Stubbed Service Interaction: Why is a rejected value being ignored?


Given a simple domain with an injected service, which is used to perform a certain validation inside a constraint, like:

package org.example.domain

class Ninja {

    String name
    String sensei
    String village

    def ninjaService

    static transients = ['ninjaService']

    static constraints = {
        ....
        village nullable:true, validator:{ val, obj, errors ->
            obj.ninjaService.validate(obj)
        }
    }
}

And a simple Spec, which stubs the service behavior, such:

package org.example.domain

import grails.test.mixin.Mock
import spock.lang.Specification
import grails.test.mixin.TestFor

import org.example.services.NinjaService

@TestFor(Ninja)
class NinjaSpec extends Specification {

    def 'Should succeed at validating the village using a stubbed method'() {
        given:
            def instance = new Ninja(village: 'Leaf')
            instance.ninjaService = Mock(NinjaService)
        when:
            instance.validate(['village'])
        then:
            1 * instance.ninjaService.validate(instance) >> { final Ninja ninja ->
                ninja.errors.rejectValue 'village', 'should.be.an.error', [].toArray(), null
                ninja.log.debug "[NINJA SERVICE MOCK] (VALIDATING) 'village' FIRED! ninja.errors['village']?.code: '${ninja.errors['village']?.code}'"
            }
        and:
            instance.errors['village']?.code == 'should.be.an.error'
    }
}

Then, what happens is that the instance.errors['village']?.code is null. Check it out:

grails test-app unit:spock -clean -echoOut NinjaSpec

| Compiling 121 source files

| Running 1 spock test... 1 of 1

--Output from Should succeed at validating the village via mock--

2016-02-04 19:51:00.219 [main] grails.app.domain.org.example.domain.Ninja
 DEBUG [NINJA SERVICE MOCK] (VALIDATING) 'village' FIRED! ninja.errors['village']?.code: 'should.be.an.error'

| Failure:  Should succeed at validating the village via mock(org.example.domain.NinjaSpec)
|  Condition not satisfied:

instance.errors['village']?.code == 'should.be.an.error'
|        |     |            |    |
|        |     null         null false
|        org.grails.datastore.mapping.validation.ValidationErrors: 0 errors
org.example.domain.Ninja : (unsaved)

    at org.example.domain.NinjaSpec.Should succeed at validating the village via mock(NinjaSpec.groovy:36)

| Completed 1 Spock test, 1 failed in 2032ms

Why doesn't the instance hold the code should.be.an.error that had been set inside the stub interaction?

Sample project @ github


Solution

  • As it seems you are not using the service but a mock, so it might not be validating anything. Try to create an instance of the service instead of mocking it.