1.0.3

Mocking MANIFEST.MF Attributes in Unit Tests

Manifests is a utility class with static only methods. The main motivation for this design is the nature of MANIFEST.MF files &mdas; they are global for the entire Java Virtual Machine. In other words, they are read-only global variables.

In order to simplify unit-testing and mocking the Manifests class has a few supplementary methods and mechanisms. The first one is inject(), which allows you to overwrite any MANIFEST.MF attribute. Let's say we have a Database class that is an adapter between a relational MySQL database and all other classes in the application:

public class Database {
  private Connection connection;
  public Database() {
    String url = Manifests.read("Jdbc-Url");
    this.connection = DriverManager.getConnection(url);
  }
  public Employee getById(int id) {
    // load Employee from the database using
    // provided ID as primary key
    return employee;
  }
}

The Database class gets JDBC URL from MANIFEST.MF. Now we want to test the class without interacting with real MySQL database. In other words we want to break this dependency and mock database behavior. We can use an in-memory H2 database and inject its URL into Manifests:

public class DatabaseTest {
  @Test
  public void makesDatabaseConnection() {
    Manifests.inject("Jdbc-Url", "jdbc:h2:mem:temp-database");
    Database db = new Database();
    Employee emp = db.getById(1);
    assertThat(emp.name(), notNullValue());
  }
}

inject() method makes MANIFEST.MF attributes available for writing.

Since Manifests is a registry of global attributes a change made to one of them in one unit test will affect all other tests, if they rely on the same attribute. Thus, it's a good practice to inject() in setUp() and clean after yourself in tearDown(). We have two convenient methods for this: snapshot() and revert(). The DatabaseTest unit test could be re-written as following:

public class DatabaseTest {
  private static byte[] manifests;
  @BeforeClass
  public static void setUp() {
    DatabaseTest.manifests = Manifests.snapshot();
    Manifests.inject("Jdbc-Url", "jdbc:h2:mem:temp-database");
  }
  @AfterClass
  public static void tearDown() {
    Manifests.revert(DatabaseTest.manifests);
  }
  @Test
  public void makesDatabaseConnection() {
    Database db = new Database();
    Employee emp = db.getById(1);
    assertThat(emp.name(), notNullValue());
  }
}

This is a good practice to do it like this, since it protects other tests against broken dependencies (which are very difficult to fix later).