Skip to content

Junit_dev_catching_modeling_events

Antonin Abhervé edited this page Sep 3, 2020 · 1 revision

Catching modeling events

When an application class is deleted, the expected behavior is that the JUnit module should automatically remove the test class that corresponds to a deleted class.

In practice, when the end-user deletes a class from his model the module is supposed to:

  • Check whether the deleted class has a corresponding test case class or not.

  • Delete the test case class if it exists.

Technically, this means that our JUnit module has to register itself to receive “Delete Element” events.

Later, when such events occur, the JUnit module is supposed to process these events and clean up the test model accordingly.

A bit of theory: module model events

Implementation

The processing of the model changes is delegated to a JUnitModelChangeHandler object. This delegate is created and registered as a model change handler in the start() session service method. It is removed in the stop() method.

Handler is used rather than listener because, our code will modify the model to remove test case classes on model classes destruction.

The JUnitModelChangeHandler class must implement the IModelChangeHandler interface in order to be registered as a model change handler. This interface has only one method which will be called each time a top level transaction is successfully committed and which receives a ModelChangeEvent object as parameter.

In our case, we are only interested in deleted elements that are classes and that have a [JUnit] stereotype dependency to a test class. For these deleted elements, we wish to delete the existing test class.

Let’s suppose, in this case, that the TestModelUpdater class is a visitor that checks that the tested model is up-to-date (and that deletes elements that have to be deleted). It extends com.modeliosoft.modelio.api.model.utils.DefaultMetamodelVisitor and redefines the operation visitModelTree to remove class stereotyped JUnit without [JunitDependency] dependency.

JUnitModelChangeHandler class

  1. Go to the org.modelio.junit.impl package

  2. Create a new class: JUnitModelChangeHandler

  3. Update the JUnitModelChangeHandler class with the following content:
    (JUnitModelChangeHandler.java source file)

 1package org.modelio.junit.impl;
 2
 3import java.util.ArrayList;
 4import java.util.List;
 5import org.modelio.api.modelio.model.IModelingSession;
 6import org.modelio.api.modelio.model.ITransaction;
 7import org.modelio.api.modelio.model.event.IElementDeletedEvent;
 8import org.modelio.api.modelio.model.event.IModelChangeEvent;
 9import org.modelio.api.modelio.model.event.IModelChangeHandler;
10import org.modelio.vcore.smkernel.mapi.MObject;
11
12
13
14public class JUnitModelChangeHandler implements IModelChangeHandler {
15
16    public void handleModelChange(IModelingSession session, IModelChangeEvent event) {
17        TestModelUpdater updater = new TestModelUpdater();
18
19        // Memorize the parents to be updated
20        List<MObject> parentsToUpdate = new ArrayList<>();
21        for (IElementDeletedEvent deletedElement : event.getDeleteEvents()) {
22            if (!parentsToUpdate.contains(deletedElement.getOldParent())) {
23                parentsToUpdate.add(deletedElement.getOldParent());
24            }
25        }
26
27        // Visit the elements to delete
28        try (ITransaction t = session.createTransaction("Update the test hierarchy")) {
29            boolean modelUpdated = false;
30            for (MObject parent : parentsToUpdate) {
31                if (((Boolean) parent.accept(updater)) == true) {
32                    modelUpdated = true;
33                }
34            }
35
36            // If the model has changed, the transaction is commited.
37            if (modelUpdated) {
38                t.commit();
39            } else {
40                t.rollback();
41            }
42        }
43    }
44}

TestModelUpdater class

  1. Go to the org.modelio.junit.impl package

  2. Create a new class: TestModelUpdater

  3. Update the TestModelUPdater class with the following content:
    (TestModelUpdater.java source file)

 1package org.modelio.junit.impl;
 2
 3import java.util.ArrayList;
 4import java.util.List;
 5
 6import org.modelio.metamodel.uml.infrastructure.Dependency;
 7import org.modelio.metamodel.uml.infrastructure.Element;
 8import org.modelio.metamodel.uml.statik.Class;
 9import org.modelio.metamodel.uml.statik.Package;
10import org.modelio.metamodel.visitors.DefaultInfrastructureVisitor;
11import org.modelio.metamodel.visitors.DefaultModelVisitor;
12
13
14public class TestModelUpdater extends DefaultModelVisitor {
15
16    public TestModelUpdater() {
17        super(new DefaultInfrastructureVisitor() {
18            @Override
19            /*
20             * Return false for all non package visits
21             */
22            public Object visitElement(Element obj) {
23                return Boolean.FALSE;
24            }
25        });
26    }
27
28    @Override
29    public Object visitPackage(Package obj) {
30        List<Class> toDelete = new ArrayList<>();
31
32        for (Class c : obj.getOwnedElement(Class.class)) {
33            // Check all owned test cases
34            if (c.isStereotyped("JUnit", "JUnit")) {
35                boolean hasTestedClass = false;
36                // Check that there is a link towards a tested class
37                for (Dependency dep : c.getDependsOnDependency()) {
38                    if (dep.isStereotyped("JUnit", "JUnitDependency")) {
39                        hasTestedClass = true;
40                    }
41                }
42
43                // No tested class means the test case must be deleted
44                if (hasTestedClass == false) {
45                    toDelete.add(c);
46                }
47            }
48        }
49
50        if (toDelete.isEmpty()) {
51            return Boolean.FALSE;
52        } else {
53            // Delete orphan test cases
54            for (Class c : toDelete) {
55                c.delete();
56            }
57            return Boolean.TRUE;
58        }
59    }
60}

JUnitLifeCycleHandler class

  1. Open the org.modelio.junit.impl.JUnitLifeCycleHandler Class

  2. Update the start() and stop() methods with the following content:
    (JUnitLifeCycleHandler.java source file)

 1...
 2public class JUnitLifeCycleHandler extends DefaultModuleLifeCycleHandler {
 3
 4    private JUnitModelChangeHandler modelChangeHandler = null;
 5...
 6    @Override
 7    public boolean start() throws ModuleException {
 8        IModuleContext context = this.module.getModuleContext();
 9        IModelingSession session = context.getModelingSession();
10        modelChangeHandler = new JUnitModelChangeHandler();
11        session.addModelHandler(modelChangeHandler);
12        return super.start();
13    }
14
15    @Override
16    public void stop() throws ModuleException {
17        IModuleContext context = this.module.getModuleContext();
18        IModelingSession session = context.getModelingSession();
19        session.removeModelHandler(modelChangeHandler);
20        modelChangeHandler = null;
21        super.stop();
22    }
23...
24
25}

Clone this wiki locally