Welcome to jMDA

To generate software automatically has been a strong ambition since the early days of software development.

jMDA is a new approach in this area. It streamlines proven, widely known and accepted open source technologies into a most comprehensible and easy to use set of Java libraries that are extremely powerful and flexible at the same time. The main purpose of jMDA is

  • to leverage a comprehensible and easy to use modelling environment,

  • to provide convenient and complete access to modelling information and

  • to make available easy to use software generator facilities.

The introduction will briefly explain the main drivers behind this project, the jMDA book provides more detailed information about the most important concepts and the open source software is available here.

sample: combine jmda.core with jmda.gen and create JUnit test suites

This example demonstrates how to combine the jmda.core and jmda.gen libraries to find all JUnit test methods in a code base and create JUnit test suites that automatically run all JUnit tests package by package. Everything you need for this example is included in the jmda.sample collection. If you wish to follow this description in more detail then please look at class TestSuiteFileWriter, that controls the overall process.

First of all we need information about all JUnit test methods in a code base. TestSuiteDataBuilder has the right static method to retrieve this:


    Map<PackageElement, Set<TypeElement>> testSuiteData =
        TestSuiteDataBuilder.build(rootDirOfFilesToProcess);


Internally this method launches an annotation processor that recursively looks for JUnit @Test annotations in all Java source code files starting from a given root directory. The next task is to generate a JUnit test suite for each package:


    TestSuiteBuilder testSuiteBuilder = new TestSuiteBuilder(testSuiteData);
    
    // create a JUnit test suite for each package containing a method with a
    // JUnit @Test annotation
    Map<PackageElement, StringBuffer> packageSuites =
        testSuiteBuilder.buildPackageTestSuites();


The generated package test suites come as StringBuffers that, if written into appropriate .java files, compile and run as they are, for example:


package de.jmda.sample.junit.a;

@org.junit.runner.RunWith(org.junit.runners.Suite.class)
@org.junit.runners.Suite.SuiteClasses
({
      de.jmda.sample.junit.a.TestA3.class,
      de.jmda.sample.junit.a.TestA2.class,
      de.jmda.sample.junit.a.TestA1.class
})
public class JUTPackageTestSuite extends org.junit.runner.Runner
{
  @java.lang.Override
  public org.junit.runner.Description getDescription()
  {
    return org.junit.runner.Description.createSuiteDescription(
        "test suite for package " + getClass().getPackage().getName());
  }

  @java.lang.Override
  public void run(org.junit.runner.notification.RunNotifier notifier)
  {
    // intentionally left blank for now
  }
}


Let's finally have a look at some details of the jmda.gen code generator that is working behind the scenes. You will see how easy it is to produce our output. We start with the central method TestSuiteBuilder.buildPackageSuite() that controls the building process:


    ClassGenerator generator = new DefaultClassGenerator();
    
    generator.setPackageName(packageName);
    generator.setVisibility(VisibilityModifier.PUBLIC);
    generator.setClassName(CLASSNAME);
    generator.setExtendsClause(Runner.class.getName());
    generator.addAnnotation(RunWith.class, sb(Suite.class.getName() ".class"));
    generator.addAnnotation(
        SuiteClasses.class, buildAnnotationParameters(typeElements));
    generator.addInstanceMethodGenerator(
        buildMethodDeclarationGeneratorGetDescription());
    generator.addInstanceMethodGenerator(buildMethodDeclarationGeneratorRun());

    StringBuffer result = generator.generate();


As you can see specifying details as package name, visibility, class name and extends clause is easy and straight forward. The definition of the annotations and methods are easy as well. Please have a look at this method for example:


  private InstanceMethodDeclarationGenerator buildMethodDeclarationGeneratorRun()
  {
    InstanceMethodDeclarationGenerator result =
        new DefaultInstanceMethodDeclarationGenerator();
    
    result.setLineIndenter(new LineIndenter("\t"1));
    result.addAnnotation(Override.class);
    result.setVisibility(VisibilityModifier.PUBLIC);
    result.setMethodName("run");
    result.addParameter(RunNotifier.class, "notifier");
    result.setMethodBody("// intentionally left blank for now");

    return result;
  }


Look at how easy it is to specify all the little details for our method. If the defaults used by a jmda.gen generator out of the box do not fit you will find an easy way to configure whatever you wish to be used instead.

No comments:

Post a Comment