My pages Projects Communities java.net
 

jmockit
Project home

If you were registered and logged in, you could join this project.

Summary A runtime instrumentation-based testing toolkit
Categories None
Owner(s) jmockit

The JMockit Testing Toolkit

JMockit is open-source software licensed under the MIT License. It is a collection of tools for use in developer testing, that is, tests written by developers using a testing framework such as JUnit or TestNG. The tools rely on the Java 5 SE instrumentation feature (the java.lang.instrument package), internally using the ASM library to modify bytecode at runtime.

There are currently six different components/tools in the JMockit toolkit, which can be used individually, as follows.


Latest version: Download Changes Author: Rogério Liesenfeld

JMockit Core

JMockit Core consists of a single class with a small set of static methods, which allow arbitrary methods and constructors of any other class to be replaced with mock implementations at runtime.

This facility can be used for writing unit or integration tests, enabling the isolation of code under test from other parts of the codebase. This approach is an alternative to the conventional use of "mock objects" as provided by tools such as EasyMock and jMock.

Both of those tools are based on java.lang.reflect.Proxy, which requires an interface to be implemented. EasyMock has an extension and jMock has a ClassImposteriser class that make it possible to mock concrete classes, by using CGLIB subclass generation. However, the classes to be mocked cannot be final. Most importantly, however, the creation of instances must be controlled, so that a mock object can be passed to client classes (the code under test). Therefore, mocked classes can't simply be instantiated with the new operator in client or test code. In effect, these limitations impose the following design restrictions on production code:

Each class which may need to be mocked in a test must either implement an interface (if using only the "regular" version of EasyMock or jMock) or not be final (if using the respective class-based extension). Instance creation methods (factories) or an IOC (dependency injection) framework must be used in order to provide the code under test with mock instances for the classes they depend on.

Another difference between JMockit Core and those tools is that its API is very small and simple (5 static methods).

By eliminating those limitations, we get the benefit that no particular design must be followed by code under test. Legacy code can be unit tested without the need for any adaptation. In short, the testability of production code becomes much less of an issue, and developers have more freedom in using Java language features.

JMockit Core depends on the JVM class redefinition mechanism exposed by java.lang.instrument. Therefore, any JUnit/TestNG tests that use it must be run under a Java 5 SE JVM. However, application and test code can still be compiled to older versions of the language.

public class JMockitCoreExampleTest
{
  public void testDoOperationAbc() {
    Mockit.redefineMethods(DependencyXyz.class, MockDependencyXyz.class);

    // ServiceAbc#doOperation(String) instantiates DependencyXyz and calls a method on it
    // with the same argument.
    Object result = new ServiceAbc().doOperation("test");

    assertNotNull(result);
  }

  public static class MockDependencyXyz
  {
    public Object doSomething(String value) {
      assertEquals("test", value);
      return new Object();
    }
  }
}

JMockit Annotations

While JMockit Core mock creation mechanism relies on coding conventions for determining which methods/constructors in a mock class are actually mocks, JMockit Annotations provides a pair of Java 5 language annotations for that purpose. In addition, constraints on the number of expected invocations for each mock can be provided.

public class JMockitAnnotationsExampleTest
{
  public void testDoOperationAbc() {
    Mockit.setUpMocks(MockDependencyXyz.class);

    // ServiceAbc#doOperation(String) instantiates DependencyXyz and calls a method on it
    // with the same argument.
    Object result = new ServiceAbc().doOperation("test");

    assertNotNull(result);
  }

  @MockClass(realClass = DependencyXyz.class)
  public static class MockDependencyXyz
  {
    @Mock(invocations = 1)
    public Object doSomething(String value) {
      assertEquals("test", value);
      return new Object();
    }
  }
}

JMockit Expectations

The approaches to define mocks provided by JMockit Core and JMockit Annotations are both powerful and simple (in terms of a small user API). However, because of the need to write separate classes and methods to define mocks, tests tend to be more lengthy than similar tests written with EasyMock/jMock would be.

JMockit Expectations therefore provide a record/replay model for writing tests, which allows for more succinct tests. In this model, a test begins by setting one or more expectations on the invocations made from code under test to its collaborators. This is the recording phase, when mocks for the collaborators are created according to the expectations set. Then starts the replay phase, when the code under test is exercised, and the invocations that actually are made on the mocked collaborators are dispatched to the corresponding mock methods and constructors. At the end of the replay phase, the expectations are verified for any missing invocations.

Naturally, the JMockit Expectations API allows for expectations to be set on any kind of method invocation (on interfaces, abstract classes, concrete final or non final classes), as well as on class instantiation through any constructors. Even private methods/constructors can have expectations defined, although at the cost of using strings for their names.

public class JMockitExpectationsExampleTest
{
  public void testDoOperationAbc()
  {
    new Expectations(true)
    {
      DependencyXyz mock;

      {
        mock.doSomething("test"); returns(new Object());
      }
    };

    // ServiceAbc#doOperation(String) instantiates DependencyXyz and calls a method on it
    // with the same argument.
    Object result = new ServiceAbc().doOperation("test");

    assertNotNull(result);
  }
}

JMockit Coverage

Existing OpenSource code coverage tools, such as Cobertura and EMMA are also less than ideal. Specifically, JMockit Coverage introduces the following benefits.

Bytecode modification done entirelly at runtime, therefore avoiding the creation of any undesirable files. This also allows code coverage information to be captured for GWT client tests, which are run from classes generated at runtime, not from any ".class" files loaded from disk. Easy integration with any test runner, such as the ones provided with JUnit or TestNG, or the embedded test runners used by Java IDEs, as long as such runners accept JVM initialization arguments (which they usually do). Integration with the JUnit Ant task is also easy. In code coverage reports, each line of production code can be accuratelly linked to the lines in test code which caused their execution, for each individual execution of each line of code. Intra-line coverage is provided, with the appropriate red/green coloring in the XHTML report for each conditionally executed line segment. This effectivelly combines line coverage and branch coverage, producing a single clear and accurate code coverage metric. An incremental test runner (currenly only for JUnit 4) is provided. When using such runner, only the individual tests which cover locally modified code are run. Any other tests included in the suite are ignored for that particular test run.


JMockit Hibernate Emulation

If you have a suite of integration tests that directly or indirectly use the Hibernate 3 APIs, you know those tests can be quite slow and fragile when compared to unit tests, because of their dependence on database access, and on a large and complex ORM library such as Hibernate. The advantages of such tests, on the other hand, are that they are relativelly easier to write (in my experience at least), and actually test a lot more (O/R mapping, HQL query strings, and the actual relational database).

The JMockit Hibernate Emulation component is a fake implementation of the Hibernate 3 Core APIs, which can be installed in place of the real implementation with a simple JVM initialization argument. When those tests run with the emulation in effect, they won't use the O/R mapping information, nor will they access any real database. All persistence operations, including HQL queries, will be executed against the equivalent of an in-memory database.


JMockit AOP

JMockit AOP supports the application of advice to any method or constructor. The usual types of advice are supported: before, after, and around.



You are viewing a mobilized version of this site...
View original page here

Mobilized by Mowser Mowser