Skip to content

Tutorial (Java instrumentation)

Bruno Santos edited this page Jul 27, 2017 · 2 revisions

Java Contexts and Dependency Injection Example

If you are or have ever developed a J2EE 6 based application, you might have noticed that CDI is a foundational aspect of such architecture. It is supported by several J2EE application servers such as Caucho's Resin, IBM's WebSphere, Oracle's Glassfish, Red Hat's JBoss and many others. CDI is similar to core Spring and Guice frameworks. CDI simplifies and sanitizes the API for DI and AOP. If you have worked with Spring or Guice, you will find CDI easy to use and easy to learn.

CDI can be used standalone and can be embedded into any application. But how do we go around testing such applications? For that we can make use of Jglue CDI-Unit project.

This COLA Tests tutorial creates an imaginary Gherkin feature for the Starship example provided in Jglue's CDI-Unit user-guide.

The sample project is available on GitHub

File: pom.xml

...
   <properties>
      <cola.version>0.4.0</cola.version>
      <cola.agent>${settings.localRepository}/com/github/bmsantos/cola-tests/${cola.version}/cola-tests-${cola.version}.jar</cola.agent>
   </properties>
...
        <dependency>
            <groupId>com.github.bmsantos</groupId>
            <artifactId>cola-tests</artifactId>
            <version>${cola.version}</version>
            <scope>test</scope>
        </dependency>
...
    </dependencies>

    <build>
...
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.18.1</version>
        <configuration>
          <argLine>-javaagent:${cola.agent}</argLine>
        </configuration>
    </plugin>
...

1. Create the business feature

Features are business goals normally defined by the customer. Gherkin based frameworks are mainly used to validate the predefined business features and customer reported bugs. In other words, even though these frameworks can be used to validate the normal unit test, its main use is to test and ensure that customer requirements are satisfied.

With this in mind, the first step is then to find out the business features of the application.

Feature: Warp Travel
  As a Starship commander
  I want to switch to warp mode
  So that I do not have to call bridge

  Scenario: Should start engine
    Given a stabilized Starship
    When the Warp Engine is initiated
    Then the Starship will warp

2. Create the COLA Test

Because we are doing BDD/TDD, the tests will be created before the application itself. So, lets start by defining the test class and methods.

File: StarshipTest.java

public class StarshipTest {

  @Feature
  private final String warpTravel =
  "Feature: Warp Travel\n"
    + "As a Starship commander\n"
    + "I want to switch to warp mode\n"
    + "So that I do not have to call bridge\n"
    + "\n"
    + "Scenario: Should start engine\n"
    + "Given a stabilized Starship\n"
    + "When the Warp Engine is initiated\n"
    + "Then the Starship will warp";

  @Given("a stabilized Starship")
  public void givenAStarship() {
  }

  @When("the Warp Engine is initiated")
  public void initiateWarpEngine() {
  }

  @Then("the Starship will warp")
  public void verifyStarshipIsWarping() {
  }
}

The first thing you'll notice is the @Feature annotation used to specify the feature created in step 1. This information will be used to execute the scenarios steps in their respective order.

Side note: Notice that you can specify the feature in its own file by making use of the @Features annotation.

Next, you'll notice the different steps annotations used to exercise the feature scenarios. In this case, these are the Given, @When and @Then annotations.

With everything in place, you should now be able to run the test by executing the following maven command:

mvn test

3. Implement the test

With COLA Tests running, it is now time to implement the actual test. For that we will make use of Jglue CDI-Unit JUnit Runner:

@RunWith(CdiRunner.class)
public class StarshipTest {

  @Feature
  private final String warpTravel =
  "Feature: Warp Travel\n"
    + "As a Starship commander\n"
    + "I want to switch to warp mode\n"
    + "So that I do not have to call bridge\n"
    + "\n"
    + "Scenario: Should start engine\n"
    + "Given a stabilized Starship\n"
    + "When the Warp Engine is initiated\n"
    + "Then the Starship will warp";

  @Inject
  private Starship starship;

  @Given("a stabilized Starship")
  public void givenAStarship() {
    assertThat(starship.isRunning()).isFalse();
  }

  @When("the Warp Engine is initiated")
  public void initiateWarpEngine() {
    starship.start();
  }

  @Then("the Starship will warp")
  public void verifyStarshipIsWarping() {
    assertThat(starship.isRunning()).isTrue();
  }
}

4. Implementation

With the test ready, it is now time to create the missing classes to be tested:

Engine.java

public interface Engine {
  void start();
  boolean isRunning();
}

WarpDrive.java

public class WarpDrive implements Engine {
  private boolean isRunning = false;

  public void start() {
    isRunning = true;
  }

  public boolean isRunning() {
    return isRunning;
  }
}

Starship.java

public class Starship {

  @Inject
  private WarpDrive engine;

  public void start() {
    engine.start();
  }

  public boolean isRunning() {
    return engine.isRunning();
  }
}

And execute the tests once again to see them pass.