Requiring External Resources Before Attempting JUnit Tests

If you have an integration test that requires external resources to be available, like a local DynamoDB server, that test should be skipped rather than fail when the resources aren’t there. In JUnit, this can be accomplished by throwing an AssumptionViolatedException from an @BeforeClass method, or better yet, with reusable ClassRules.

A ClassRule runs like an @BeforeClass method; once before the entire suite of test methods in the class. @Rule is analogous to @Before and runs before each test method. In this case, we use the ClassRule to check for resources along with preparing the use of them (see the call to .client()). If the AWS client cannot connect to a local DynamoDB server, it will throw an AssumptionViolatedException causing JUnit to mark the test class as skipped rather than failed.

-------------------------------------------------------
 T E S T S
 -------------------------------------------------------
 Running DynamoDaoIT
 Tests run: 1, Failures: 0, Errors: 0, Skipped: 1, Time elapsed: 0.054 sec - in DynamoDaoIT

 Results :

 Tests run: 1, Failures: 0, Errors: 0, Skipped: 1
import java.util.Properties;

import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;

public class DynamoMappingsDaoIT {
  private static final Properties CONFIG = // load your app test properties;

  /**
   * This runs like an @BeforeClass method (@Rule is analogous to @Before).  If the AWS
   * client cannot connect to a local DynamoDB server, it will throw an
   * AssumptionViolatedException causing JUnit to mark the test class as <code>skipped</code>
   * rather than <code>failed</code>.
   */
  @ClassRule
  public static final DynamoDbRule DYNAMO_DB_RULE = new DynamoDbRule().withLocalPort(CONFIG.getString("dynamo.port"));
  
  private DynamoDAO dao;

  @Before
  public void setUp() {
    // Create this here to give the class rule a chance to run first.
    dao = new DynamoDAO(CONFIG);
  }

  @Test
  public void shouldUseDynamo() {
    // test safely knowing that your dynamo client is connected
    mappingDAO.find(1);
    assertNotNull(DYNAMO_DB_RULE.client());
  }
}
import com.amazonaws.AmazonClientException;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.google.common.base.Strings;
import org.junit.Assume;
import org.junit.rules.ExternalResource;

public class DynamoDbRule extends ExternalResource {
  private String endpoint = "http://localhost:8000";
  private String accessKeyId;
  private String secretKey;
  private int maxRetry = 0;
  private AmazonDynamoDBClient client;

  public DynamoDbRule() { }

  public DynamoDbRule withLocalPort(final int port) {
    return withEndpoint("http://localhost:" + port);
  }

  public DynamoDbRule withEndpoint(final String endpoint) {
    this.endpoint = endpoint;
    return this;
  }

  public DynamoDbRule withAccessKeyId(final String accessKeyId) {
    this.accessKeyId = accessKeyId;
    return this;
  }

  public DynamoDbRule withSecretKey(final String secretKey) {
    this.secretKey = secretKey;
    return this;
  }

  public DynamoDbRule withMaxRetry(final int maxRetry) {
    this.maxRetry = maxRetry;
    return this;
  }

  public AmazonDynamoDBClient client() {
    if (client == null) {
      throw new IllegalStateException("Run before() to create the client");
    }
    return client;
  }

  @Override
  protected void before() throws Throwable {
    ClientConfiguration clientConfig = new ClientConfiguration().withMaxErrorRetry(maxRetry);

    if (Strings.isNullOrEmpty(accessKeyId) || Strings.isNullOrEmpty(secretKey)) {
      client = new AmazonDynamoDBClient(clientConfig);
    } else {
      AWSCredentials creds = new BasicAWSCredentials(accessKeyId, secretKey);
      client = new AmazonDynamoDBClient(creds, clientConfig);
    }
    client.setEndpoint(endpoint);

    try {
      client.listTables(1);
    } catch (AmazonClientException e) {
      Assume.assumeNoException(e);
    }
  }
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s