How to programatically create a detailed event like z3c.form does?

121 Views Asked by At

I have a simple event handler that looks for what has actually been changed (it's registered for a IObjectModifiedEvent events), the code looks like:

def on_change_do_something(obj, event):
    modified = False
    # check if the publication has changed
    for change in event.descriptions:
        if change.interface == IPublication:
            modified = True
            break

    if modified:
        # do something

So my question is: how can I programmatically generate those descriptions? I'm using plone.app.dexterity everywhere, so z3c.form is doing that automagically when using a form, but I want to test it with a unittest.

2

There are 2 best solutions below

1
On BEST ANSWER

event.description is nominally an IModificationDescription object, which is essentially a list of IAttributes objects: each Attributes object having an interface (e.g. schema) and attributes (e.g. list of field names) modified.

Simplest solution is to create a zope.lifecycleevent.Attributes object for each field changed, and pass as arguments to the event constructor -- example:

# imports elided...

changelog = [
    Attributes(IFoo, 'some_fieldname_here'),
    Attributes(IMyBehaviorHere, 'some_behavior_provided_fieldname_here',
    ]
notify(ObjectModifiedEvent(context, *changelog)
0
On

I may also misunderstood something, but you may simple fire the event in your code, with the same parameters like z3c.form (Similar to the comment from @keul)?

After a short search in a Plone 4.3.x, I found this in z3c.form.form:

def applyChanges(self, data):
    content = self.getContent()
    changes = applyChanges(self, content, data)
    # ``changes`` is a dictionary; if empty, there were no changes
    if changes:
        # Construct change-descriptions for the object-modified event
        descriptions = []
        for interface, names in changes.items():
            descriptions.append(
                zope.lifecycleevent.Attributes(interface, *names))
        # Send out a detailed object-modified event
        zope.event.notify(
            zope.lifecycleevent.ObjectModifiedEvent(content, *descriptions))
    return changes

You need two testcases, one which does nothing and one which goes thru your code.

applyChanges is in the same module (z3c.form.form) it iterates over the form fields and computes a dict with all changes.

You should set a break point there to inspect how the dict is build.

Afterwards you can do the same in your test case.

This way you can write readable test cases.

def test_do_something_in_event(self)

    content = self.get_my_content()
    descriptions = self.get_event_descriptions()

    zope.event.notify(zope.lifecycleevent.ObjectModifiedEvent(content, *descriptions))

    self.assertSomething(...)        

IMHO mocking whole logic away may be a bad idea for future, if the code changes and probably works completely different, your test will be still fine.