Search code examples
grailsspock

Disabling Event Listeners In Spock Unit Test


I'm trying to figure out if it's possible to disable event listeners in a Spock Unit Test.

I'm working on a service in a Grails app that utilizes event listeners upon saving a new object (an organization). Using a publisher and subscriber here allows me to create a new RecipientGroup and make it the organization's default group in a specific order that seems to work better with Hibernate.

Here's the save method and subscriber to the event from the save method:

class MyService {

    @Transactional
    @Publisher(Event.ORGANIZATION_CREATED)
    Organization save(Organization organization) {
    
        if (Organization.findByName(organization.name))
            throw new Exception('organization.create.exists')
    
        organization.save(failOnError: true)
        sendSignupNotification(organization)
        metricsService.createDefaults(organization)
    
        return organization
    }

    @Transactional
    @Subscriber(Event.ORGANIZATION_CREATED)
    void createDefaultRecipientGroup(Organization org) {
        Organization organization = organizationDataService.get(org.id)
        organization.defaultGroup = new RecipientGroup(organization: organization).save(failOnError: true, flush: true)
        organization.save(flush: true)
    }
}

However, this breaks some of my unit tests, cannot invoke method get() on a null object in reference to organizationDataService.get() in my subscriber method. I would prefer to split up the creation of the object and these extra tasks into different unit tests. So, I'm trying to figure out if there is a way to disable the publisher or listener during my test. Also open to other ideas on how to structure this.

Here's the test:

void "test that the organization save method correctly sets up default Recipient group"() {
    setup:
    String foo = "foo"

    when:
    Organization organization = new Organization(name: foo)
    service.save(organization)

    and: "the default recipient group is created"
    RecipientGroup.findByOrganization(organization)

    and:
    1 * mockTextService.send(_, true)

    and:
    0 * _
}

Thanks in advance!


Solution

  • Revisiting this because I have a viable workaround to this now. Unit tests appear to only pick up publisher/subscriber annotations in the class being tested. Moving the subscriber into a different service is allowing the desired behavior of disabling the event listener for testing, so I can then build out separate unit tests for the two methods in question:

    class MyService1 {
    
        @Transactional
        @Publisher(Event.ORGANIZATION_CREATED)
        Organization save(Organization organization) {
        
            if (Organization.findByName(organization.name)) throw new Exception('organization.create.exists')
        
            organization.save(failOnError: true)
            sendSignupNotification(organization)
            metricsService.createDefaults(organization)
        
            return organization
        }
    }
    
    
    class MyService2 {
    
        @Transactional
        @Subscriber(Event.ORGANIZATION_CREATED)
        void createDefaultRecipientGroup(Organization org) {
            Organization organization = organizationDataService.get(org.id)
            organization.defaultGroup = new RecipientGroup(organization: organization).save(failOnError: true, flush: true)
    
            organization.save(flush: true)
        }
    }