Search code examples
testingplonedexterity

Testing Dexterity content creation in isolation


For a project, I have a complex master object that contains a number of subcomponents. Set up of these objects is controlled by a Constructor interface, which I bind to various lifecycle & workflow events, like so:

@grok.subscribe(schema.ICustomFolder, lifecycleevent.IObjectAddedEvent)
def setup_custom_folder(folder, event):
    interfaces.IConstructor(folder).setup()

@grok.subscribe(schema.ICustomFolder, lifecycleevent.IObjectModifiedEvent)
def setup_custom_folder(folder, event):
    interfaces.IConstructor(folder).update()

What I'd like to be able to do is test the Constructor methods without relying on the event handlers. I've tried doing this by creating objects directly to avoid the lifecycle events:

def test_custom_item_constructor(self):
    master = createContent('model.master_object', 
        needed_attribute = 2
    )
    folder = createContent('model.custom_folder',
        __parent__ = master
    )
    self.assertEqual(0, len(folder))
    constructor = interfaces.IConstructor(folder)
    constructor.setup()
    self.assertEqual(2, len(folder))

The setup method creates a number of items inside the Custom_Folder instance, dependent on the provided attribute on the master object. However, this is hanging, which I think is due to neither object actually belonging to the site, so there's no acquisition of permissions. I can get this by changing the createContent on the master object to createContentInContainer and adding it to the appropriate part of the test site, but that triggers all of the lifecycle events, which end up doing the Constructor calls, which doesn't let me test them in isolation.

I've tried using mock objects for this, but that got messy dealing with the content creation that is meant to occur during the Constructor .setup.

What's the best way to approach this?


Solution

  • I'm not sure if this is the best way, but I managed to get the result I wanted by disabling the relevant event handlers first, and then creating the content properly within the site:

    def test_custom_item_constructor(self):
        zope.component.getGlobalSiteManager().unregisterHandler(
            adapters.master.constructor.setup_masterobject,
            required=[schema.IMasterObject, lifecycleevent.IObjectAddedEvent]
        )
        zope.component.getGlobalSiteManager().unregisterHandler(
            adapters.custom.constructor.setup_customfolder,
            required=[schema.ICustomFolder, lifecycleevent.IObjectAddedEvent]
        )
        master = createContentInContainer(self.portal, 'model.master_object', 
            needed_attribute = 2
        )
        folder = createContentInContainer(master, 'model.custom_folder',
            __parent__ = master
        )
        self.assertEqual(0, len(folder))
        constructor = interfaces.IConstructor(folder)
        constructor.setup()
        self.assertEqual(2, len(folder))
    

    This was enough to disengage the chain of events triggered by the addition of a new master object.