Processing commands: model transformation

Step 1: Checking for an already existing test case

The main objective of the “Create a test case” command when applied to a Class object MyClass is to create a MyClassTest test class in the test model and at the proper place in the test model package hierarchy.

However, it may be that a test class already exists for MyClass, in which case the Module is not expected to create an additional test case class nor to delete and replace the existing one, but is rather supposed to update the existing test case class, automatically identifying the required changes.

This advanced behavior cannot be presented here, as it would lead to listing a masterpiece of code which is outside our scope. However, note how the implementation of this advanced behavior is simplified by our design choice of adding stereotyped dependencies between the MyClass class and its MyClassTest counterpart in the test model. By navigating this dependency when it exists, the module developer can easily find MyClassTest from MyClass and perform an update instead of a pure creation.

For now, as a simplification, let’s decide that the command will fail if called on a class that already has a corresponding test class and that it will only display an error by popping an error dialog box to the end-user.

So, first have a look at this first part of the code: detecting an existing test class if one exists. We make the hypothesis that the code is implemented in the CreateTestCaseWizardDialog class and that this class also contains two attributes initialized from its constructor that contain the class to test and the JUnit Module object itself. Other designs for the code are, of course, possible, but this one makes the presentation easier to understand.

 1import org.eclipse.swt.SWT;
 2import org.eclipse.swt.widgets.Display;
 3import org.eclipse.swt.widgets.MessageBox;
 4import org.modelio.api.module.IModule;
 5import org.modelio.api.module.commands.DefaultModuleContextualCommand;
 6import org.modelio.metamodel.uml.infrastructure.Dependency;
 7import org.modelio.metamodel.uml.infrastructure.Element;
 8import org.modelio.metamodel.uml.infrastructure.Stereotype;
 9import org.modelio.metamodel.uml.statik.Class;
10
11...
12
13public class CreateTestCaseWizardDialog extends DefaultModuleContextualCommand{
14
15    private Class  classToTest;
16    private IModule junitModule;
17
18    public boolean createTestCase(Element element, IModule module) {
19        classToTest = (Class) element;
20        junitModule = module;
21        // Check that there is no already existing test class
22        for (Dependency dep : classToTest.getDependsOnDependency()) {
23            if (dep.isStereotyped(MODULE_NAME,  "JUnit")) {
24                MessageBox box = new MessageBox(
25                        Display.getCurrent().getActiveShell(),SWT.ICON_ERROR|SWT.OK);
26                box.setText("Error");
27                box.setMessage("Command cannot be applied: class already has a test case");
28                box.open();
29                return false;
30            }
31            ...
32        }
33    }
34    ...
35}
36

The previous code simply gets the dependencies that have their origin on the class for which the test is created and which are stereotyped JUnit. If such a dependency is found, the code pops an error message and returns false.

To get the dependencies, we use the getDependsOnDependency() accessor method that returns the list of these dependencies. If no dependencies exist, the returned list is still valid but empty (no null value returned).

The name of the accessor method to use can be easily found from the metamodel. It is the name of the role of the association that you want to reach, prefixed by get. This kind of accessor is used to read the associated values. This is detailed in the [Modelio MDA API wiki][10].

However, the returned list will contain all the dependencies, even those that

are not <<JUnit>> ones and consequently of no interest to us. This is why we have to filter the returned list for the <<JUnit>> stereotyped dependencies only thanks to the help of the select method and StereotypeFilter object.

Once we have checked that no test class already exists for the selected class, we have to:

  1. Create/update the test model package hierarchy in order to have the proper place to put the test class.
  2. Create and name the test class.
  3. Add a <<JUnit>> stereotype to the test class.
  4. Link the test class to the tested class.
  5. Add the test class features (methods and attributes) as required.

Step 2: Creating/updating the test model hierarchy

What we have to do at this point is to create a test class and to place it in the proper test package.

Creating the class is quite obvious because Modelio MDA API provides convenient factory creation methods to create UML elements. However, in the test model, we have to find the proper test package if it already exists or create it if it does not. The objective is to establish a test model hierarchy of packages which is parallel to the initial model.

Let’s see what the code can look like:

 1class CreateTestCaseWizardDialog {
 2    . . .
 3    public IPackage getTestCaseParentPackage (IClass classToTest)
 4    {
 5        . . .
 6        try {
 7        IModelingSession session = Modelio.getInstance().getModelingSession();
 8        IUmlModel model = session.getModel();
 9        
10        ITransaction t = session.createTransaction("Create test hierarchy");
11        . . .
12        
13        session.commit(t);
14        } catch ( ) {
15        . . .
16        }
17    }
18    . . .
19}

Step 3: Creating the test model class

Once the parent package of the test class to create is known and accessible, the creation of the test case class is easy.

Let’s see what the code can look like:

 1class CreateTestCaseWizardDialog {
 2    . . .
 3    private IClass  classToTest;  // the IClass object for which test has to be created
 4    private IMdac junitMdac;
 5    . . .
 6    public boolean createTestCase( )
 7    {
 8        
 9        . . .
10        
11        try {
12        IModelingSession session = Modelio.getInstance().getModelingSession();
13        UmlModel model = session.getModel();
14        
15        ITransaction t = session.createTransaction("Create a test case");
16        
17        // Build the test class name
18        String testClassName = classToTest.getName() + "Test";
19        
20        // Get the package parent of the test class
21        // The getTestCaseParentPackage() returns an IPackage
22        // which is created on the fly if necessary (ok as we are in a transaction)
23        IPackage testCaseParentPackage = getTestCaseParentPackage(classToTest);
24        
25        // Create the class using the convenient factory method createClass()
26        model.createClass(testClassName, testCaseParentPackage);
27        
28        session.commit(t);
29        
30        } catch ( ) {
31        . . .
32        }
33    
34    }
35    . . .
36}

Step 4: Adding the <<JUnit>> stereotype to the test model class

In order to add a <<JUnit>> stereotype to the created test class, we need to find the IStereotype object.

A stereotype is an UML extension brought to the modeling session environment by the deployed Modules (in our case, the JUnit Module).

Let’s have a look at how to do this in the following code fragment:

 1class CreateTestCaseWizardDialog {
 2    . . .
 3    public boolean stereotypeTestCase( IClass testClass )
 4    {
 5        . . .
 6        try {
 7            IModelingSession session = Modelio.getInstance().getModelingSession();
 8            IUmlModel model = session.getModel();
 9            
10            ITransaction t = session.createTransaction("Stereotype a test case");
11            
12            // Find the &lt;&lt;JUnit&gt;&gt; stereotype
13            IStereotype s;
14            
15            try {           
16                s = session.getMetamodelExtensions().getStereotype(IClass.class,
17                "JUnit");
18                // Add the stereotype to the class
19                testClass.addExtension( s );
20            }
21            catch (StereotypeNotFoundException e) {
22                MessageBox box = new MessageBox(
23                    Display.getCurrent().getActiveShell(),SWT.ICON_ERROR|SWT.OK);
24                box.setText("Error");
25                box.setMessage("Stereotype JUnit not found, check your installation");
26                box.open();
27                return false;
28            }
29            session.commit(t);
30        } catch ( ) {
31        . . .
32        }
33        . . .
34    }
35}

Note that the getStereotype() method requires both the name of the stereotype to find and the target metaclass. An exception is thrown if no stereotype applicable to the metaclass is found.

Introducing a variant for creating the test class

In the particular case of creating a class, the modeling session factory proposes an advanced createClass variant that takes the name of an applicable stereotype to the created class. This stereotype is then automatically added by the factory method to the created class. This would give the following code for creating the test class. This code does not require that we get the IStereotype object.

 1public boolean createTestCase( )
 2{
 3 . . .
 4 try {
 5  IModelingSession session = Modelio.getInstance().getModelingSession();
 6  IUmlModel model = session.getModel();
 7  ITransaction t = session.createTransaction("Create a test case");
 8  // Build the test class name
 9  String testClassName = classToTest.getName() + "Test";
10         // Get the package parent of the test class
11  // The getTestCaseParentPackage() returns an IPackage
12                // which is created on the fly if necessary (ok as we are in a transaction)
13  IPackage testCaseParentPackage = getTestCaseParentPackage(classToTest);
14  // Create the class
15  model.createClass(testClassName, testCaseParentPackage, "JUnit");
16  session.commit(t);
17 } catch ( ) {
18  . . .
19 }

Step 5: Establishing the <<JUnit>> dependency link

In order to add a <<JUnit>> dependency link to the created test class, we need to create the IDependency object.

A dependency is an UML extension brought to the Modeling Session environment by the deployed Modules (in our case, the JUnit Module).

Let’s have a look at how to do this in the following code fragment:

 1class CreateTestCaseWizardDialog {
 2 . . .
 3 public boolean stereotypeTestCase( IClass testClass )
 4 {
 5  . . .
 6  try {
 7   IModelingSession session = Modelio.getInstance().getModelingSession();
 8   IUmlModel model = session.getModel();
 9   ITransaction t = session.createTransaction("Stereotype a test case");
10   // Create the class
11  IClass testClass = model.createClass(                                                  
12            testClassName, testCaseParentPackage, "JUnit");
13                try {
14                        //Create the dependency
15                        IDependency dep = model.createDependency(
16                                                  
17                    classToTest,testClass,"JUnitDependency");
18   }
19   catch (StereotypeNotFoundException e) {
20   MessageBox box = new MessageBox(
21      Display.getCurrent().getActiveShell(),SWT.ICON_ERROR|SWT.OK);
22                                box.setText("Error");
23     box.setMessage("Stereotype JUnitDependency not found, check your installation");
24     box.open();
25     Modelio.err.println("Stereotype JUnitDependency not found, check your installation");
26     return false;
27   }
28   session.commit(t);
29  } catch ( ) {
30  . . .
31 }
32. . .
33}

Step 6: Adding the test class features

 1class CreateTestCaseWizardDialog {
 2 . . .
 3 public boolean stereotypeTestCase( IClass testClass )
 4 {
 5  . . .
 6  try {
 7   IModelingSession session = Modelio.getInstance().getModelingSession();
 8   IUmlModel model = session.getModel();
 9   ITransaction t = session.createTransaction("Stereotype a test case");
10   . . .
11try {
12     // Create the class
13     IClass testClass = model.createClass(
14                                                  
15            testClassName, testCaseParentPackage, "JUnit");
16 
17  ...
18                  //Create the test operation
19                                      
20model.createOperation("testOperation1", testClass);
21   }
22   catch (StereotypeNotFoundException e) {
23   MessageBox box = new MessageBox(Display.getCurrent().getActiveShell(),SWT.ICON_ERROR|SWT.OK);
24                                box.setText("Error");
25     box.setMessage("Stereotype JUnit not found, check your installation");
26     box.open();
27     return false;
28   }
29   session.commit(t);
30  } catch ( ) {
31  . . .
32 }
33. . .
34}

<< Previous Index Next >>