unit-testinggrailsgrails-3.0grails-3.3

Grails 3 Controller Unit Testing: Domain class validation not working in a ControllerUnitTest


I'm upgrading an inherited Grails 2 app to 3.3.10. We have controller methods that rely on Domain class validation to control logic flow, but we can't get DomainClass validation to work in ControllerUnitTests when running in Grails 3

For example, Gift and RecipientAddress are DomainClasses and RecipientAddress.hasErrors() is used to validate against the RecipientAddress constraints.

    def confirmAddress() {
        Gift gift = Gift.get(params.giftId)
        if (!gift) {
            render(view: "index", model: [invalid: true])
            return
        }

        recipientAddress = recipientAddressService.storeAddressInformation(params, gift)
        if (recipientAddress.hasErrors()) {
            render(view: "index", model: getAddressErrorModel(gift, recipientAddress))
            return;
        } else {
            return [
                    recipientAddress    : recipientAddress,
                    gift                : gift
            ]
        }
    }

In the following test, when I debug the controller method it does everything expected but recipientAddress.hasErrors() always returns true and the test fails.

ie:

@Transactional
class GiftDetailsControllerTest extends Specification implements ControllerUnitTest<GiftDetailsController> {

    @Shared
    @AutoCleanup
    SimpleMapDatastore dataStore = new SimpleMapDatastore([ConnectionSource.DEFAULT, "reporting"],
            RecipientAddress, Gift)

    def setup() {
        controller.recipientAddressService = Mock(RecipientAddressService)
    }

    void "test RecipientAddress Bad PhoneNumber"() {
        given:
        RecipientAddress recipientAddress = new RecipientAddress(
                phone: '123-456-789'
        )

        UnitTestDataFactory dataFactory = UnitTestDataFactory.getDataFactory()
        Gift gift = dataFactory.getMockGift()
        gift.save()

        params.giftId = gift.id

        when:
        recipientAddress.validate()
        controller.confirmAddress()

        then:
        recipientAddress.hasErrors()
        recipientAddress.getErrors().getFieldError('phone')
        1 * controller.recipientAddressService.storeAddressInformation(params, gift) >> recipientAddress
        view == '/giftDetails/index'
    }
}

Implementing DataTest ie: ...implements ControllerUnitTest<GiftDetailsController>, DataTest { fixes the DomainClass validation, but breaks the controllers ability to get the saved Gift.

Is there a way get DomainClass validation working in a ControllerUnit test?


Fix

Implemented DataTest with mockDomains and had to remove the custom dataStore.

@Transactional
class GiftDetailsControllerTest extends Specification implements ControllerUnitTest<GiftDetailsController>, DataTest {

//    @Shared
//    @AutoCleanup
//    SimpleMapDatastore dataStore = new SimpleMapDatastore([ConnectionSource.DEFAULT, "reporting"], RecipientAddress, Gift)

    void setupSpec() {
        mockDomains RecipientAddress, Gift
    }

....


Solution

  • Is there a way get DomainClass validation working in a ControllerUnit test?

    Yes.

    You probably want something like this...

    class GiftDetailsControllerTest extends Specification implements ControllerUnitTest<GiftDetailsController>, DataTest {
    
        Class[] getDomainClassesToMock() {
            [Gift]
        }
    
        // ...
    }