Java Programming Tutorials

Java programming tutorials with many code examples!

JUnit fixtures – code reuse

What are JUnit Fixtures and how to use them? In this lesson we’re going to work through different types of fixtures and see how pros do it. Lets test!

Fixtures to reuse code

Very often initialization code is the same in many test methods. So it makes sense to move that part to separate method and reuse. This is where JUnit fixtures step in. They are methods annotated with JUnit annotations that have the following meaning:

  • @Before: execute before each test,
  • @After: execute after each test,
  • @BeforeClass: execute before all tests in the class,
  • @AfterClass: execute after all tests in the class.

Also object instantiation that is outside tests is executed before each test, so it has pretty much the same meaning as methods annotated with @Before. The difference is that it is more comfortable to execute more code in annotated method.

Enough talking, here’s an example:

package com.farenda.junit;

import org.junit.*;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

public class FixturesTest {

    private User user;
    private User jon = new User("Jon", "Snow");

    @BeforeClass
    public static void executedOnceBeforeAllTestsInThisClass() {
        System.out.println("Instantiating a heavy resource.");
    }

    @Before
    public void calledBeforeEachTest() {
        System.out.println("Recreating User!");
        user = new User("James", "Brown");
    }

    @After
    public void calledAfterEachTest() {
        System.out.println("Cleanup after a test!");
    }

    @AfterClass
    public static void executedOnceAfterAllTestsInThisClass() {
        System.out.println("Disposing a heavy resource.");
    }

    @Test
    public void shouldHaveJonSnow() {
        System.out.println("Checking Jon Snow.");
        assertNotNull(jon);
        assertEquals("Jon", jon.getFirstName());
        assertEquals("Snow", jon.getLastName());
    }

    @Test
    public void shouldHaveUser() {
        System.out.println("Executing shouldHaveUser test");
        assertNotNull(user);
        assertEquals("James", user.getFirstName());
        assertEquals("Brown", user.getLastName());

        // this affects only current method:
        user.setFirstName("Michael");
        user.setLastName("Jackson");
    }

    @Test
    public void shouldHaveRecreatedUser() {
        System.out.println("Executing shouldHaveRecreatedUser test");
        assertNotNull(user);
        assertEquals("James", user.getFirstName());
        assertEquals("Brown", user.getLastName());
    }
}

Note that methods annotated with @BeforeClass and @AfterClass have to be static, because the code is executed before/after test class is instantiated.

Here’s the result of running the above test:

Instantiating a heavy resource.
Recreating User!
Checking Jon Snow.
Cleanup after a test!
Recreating User!
Executing shouldHaveUser test
Cleanup after a test!
Recreating User!
Executing shouldHaveRecreatedUser test
Cleanup after a test!
Disposing a heavy resource.

How pros do it

The first example demonstrates usage of common code execution @Before and @After each test. The following code is taken from Apache Hadoop TestDiskError:

package org.apache.hadoop.hdfs.server.datanode;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

// ... imports truncated for brevity

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * Test that datanodes can correctly handle errors during block read/write.
 */
public class TestDiskError {

  private FileSystem fs;
  private MiniDFSCluster cluster;
  private Configuration conf;

  @Before
  public void setUp() throws Exception {
    conf = new HdfsConfiguration();
    conf.setLong(DFSConfigKeys.DFS_BLOCK_SIZE_KEY, 512L);
    cluster = new MiniDFSCluster.Builder(conf).numDataNodes(1).build();
    cluster.waitActive();
    fs = cluster.getFileSystem();
  }

  @After
  public void tearDown() throws Exception {
    cluster.shutdown();
  }

  // ... other tests removed for brevity

  /**
   * Check that the permissions of the local DN directories are as expected.
   */
  @Test
  public void testLocalDirs() throws Exception {
    Configuration conf = new Configuration();
    final String permStr = conf.get(
      DFSConfigKeys.DFS_DATANODE_DATA_DIR_PERMISSION_KEY);
    FsPermission expected = new FsPermission(permStr);

    // Check permissions on directories in 'dfs.datanode.data.dir'
    FileSystem localFS = FileSystem.getLocal(conf);
    for (DataNode dn : cluster.getDataNodes()) {
      try (FsDatasetSpi.FsVolumeReferences volumes =
          dn.getFSDataset().getFsVolumeReferences()) {
        for (FsVolumeSpi vol : volumes) {
          String dir = vol.getBasePath();
          Path dataDir = new Path(dir);
          FsPermission actual = localFS.getFileStatus(dataDir).getPermission();
          assertEquals("Permission for dir: " + dataDir + ", is " + actual +
              ", while expected is " + expected, expected, actual);
        }
      }
    }
  }

  // ... other tests removed for brevity
}

@BeforeClass and @AfterClass annotations usually are used in abstract base tests, but here’s an example test (MemoryLeakTest) from Drools JBPM that is using them in test class:

package org.jbpm.memory;

import static org.junit.Assert.*;

import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

// ... imports truncated for brevity

public class MemoryLeakTest {

    private static final Logger logger = LoggerFactory.getLogger(MemoryLeakTest.class);

    private static HashMap<String, Object> testContext;
    private Environment env = null;

    private static final String PROCESS_NAME = "RuleTaskWithProcessInstance";

    @BeforeClass
    public static void beforeClass() {
        testContext = setupWithPoolingDataSource(JBPM_PERSISTENCE_UNIT_NAME);
    }

    @AfterClass
    public static void afterClass() {
        cleanUp(testContext);
    }

    @Before
    public void before() {
        env = createEnvironment(testContext);
    }

    @Test
    public void findEventSupportRegisteredInstancesTest() {
        // setup
        KieBase kbase = createKnowledgeBase();

        for( int i = 0; i < 3; ++i ) {
            createKnowledgeSessionStartProcessEtc(kbase);
        }

        KieBaseEventSupport eventSupport = (KieBaseEventSupport) getValueOfField("eventSupport", KnowledgeBaseImpl.class, kbase);
        assertEquals( "Event listeners should have been detached", 0, eventSupport.getEventListeners().size());
    }

    private KieBase createKnowledgeBase() {
    // ... other code truncated for brevity
    }
}
Share with the World!