Merge pull request #11 from myrle-krantz/develop

Added support for creating suites of tests using junit @Suite annotat…
diff --git a/src/main/java/io/mifos/core/test/fixture/TenantDataStoreContextTestRule.java b/src/main/java/io/mifos/core/test/fixture/TenantDataStoreContextTestRule.java
index 5face82..a91178f 100644
--- a/src/main/java/io/mifos/core/test/fixture/TenantDataStoreContextTestRule.java
+++ b/src/main/java/io/mifos/core/test/fixture/TenantDataStoreContextTestRule.java
@@ -17,28 +17,57 @@
 
 import io.mifos.core.test.env.TestEnvironment;
 import org.junit.rules.ExternalResource;
+import org.springframework.util.Assert;
+
+import javax.annotation.Nullable;
 
 /**
+ * Use an instance of class as a @Classrule in component tests which require a tenant
+ * initialized in the data store(s) used by the service under test. It will generate a
+ * tenant name, create any necessary data structures, and set the tenant context for
+ * REST calls into the service.
+ *
+ * Example:
+ * <pre>
+ * {@code
+ * @literal @ClassRule
+ *     public final static TenantDataStoreContextTestRule tenantDataStoreContext
+ *         = TenantDataStoreContextTestRule.forRandomTenantName(cassandraInitializer, mariaDBInitializer);
+ * }
+ * </pre>
+ *
+ *
  * @author Myrle Krantz
  */
 @SuppressWarnings({"unused", "WeakerAccess"})
 public class TenantDataStoreContextTestRule extends ExternalResource {
-
-  private final String tenantName;
-  private final DataStoreTenantInitializer[] dataStoreTenantInitializers;
+  @Nullable
+  private String tenantName;
+  @Nullable
   private TenantDataStoreTestContext tenantDataStoreTestContext;
 
+  private final boolean generateTenantName;
+  private final DataStoreTenantInitializer[] dataStoreTenantInitializers;
+
   private TenantDataStoreContextTestRule(
-          final String tenantName,
-          final DataStoreTenantInitializer... dataStoreTenantInitializers) {
+      final String tenantName,
+      final DataStoreTenantInitializer... dataStoreTenantInitializers) {
+    this.generateTenantName = false;
     this.tenantName = tenantName;
     this.dataStoreTenantInitializers = dataStoreTenantInitializers;
   }
 
+  private TenantDataStoreContextTestRule(
+      final DataStoreTenantInitializer... dataStoreTenantInitializers) {
+    this.generateTenantName = true;
+    this.tenantName = null;
+    this.dataStoreTenantInitializers = dataStoreTenantInitializers;
+  }
+
   public static TenantDataStoreContextTestRule forRandomTenantName(
           final DataStoreTenantInitializer... dataStoreTenantInitializers)
   {
-    return new TenantDataStoreContextTestRule(TestEnvironment.getRandomTenantName(), dataStoreTenantInitializers);
+    return new TenantDataStoreContextTestRule(dataStoreTenantInitializers);
   }
 
   public static TenantDataStoreContextTestRule forDefinedTenantName(
@@ -50,11 +79,23 @@
 
   @Override
   public void before() {
+    if (generateTenantName) {
+      //Generate the tenantName in the before method rather than in the constructor.
+      //If this rule is used as a static variable in a test parent class, in the context of a
+      //test suite consisting of multiple test classes, the tenantName should
+      //be regenerated for each test class run.  This has two advantages:
+      //1.) The database initialization isn't re-executed for the tenant. Re-executing database
+      //    initialization fails, causing all tests after the first one to fail.
+      //2.) Each test class is executed in the context of a new tenant, thus mostly isolating
+      //    each test class from any side-effects produced by the others.
+      tenantName = TestEnvironment.getRandomTenantName();
+    }
     tenantDataStoreTestContext = TenantDataStoreTestContext.forDefinedTenantName(tenantName, dataStoreTenantInitializers);
   }
 
   @Override
   public void after() {
+    Assert.notNull(tenantDataStoreTestContext);
     tenantDataStoreTestContext.close();
   }
 
diff --git a/src/main/java/org/junit/rules/RunExternalResourceOnce.java b/src/main/java/org/junit/rules/RunExternalResourceOnce.java
new file mode 100644
index 0000000..c25f95c
--- /dev/null
+++ b/src/main/java/org/junit/rules/RunExternalResourceOnce.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.junit.rules;
+
+/**
+ * Use this to "decorate" a resource to ensure that it is
+ * initialized exactly once and de-initialized exactly once
+ * when used in a suite of multiple tests. This is mostly
+ * useful when creating test suites.
+ *
+ * Example:
+ *
+ * <pre>
+ * {@code
+ * @literal @ClassRule
+ *     public static TestRule orderClassRules = RuleChain
+ *         .outerRule(new RunExternalResourceOnce(testEnvironment))
+ *         .around(new RunExternalResourceOnce(cassandraInitializer))
+ *         .around(new RunExternalResourceOnce(mariaDBInitializer));
+ * }
+ * </pre>
+ *
+ * @author Myrle Krantz
+ */
+@SuppressWarnings("unused")
+public class RunExternalResourceOnce extends ExternalResource {
+  private final ExternalResource decoratedResource;
+  private int callCount = 0;
+
+  public RunExternalResourceOnce(final ExternalResource decoratedResource) {
+    //I love to decorate. Don't you?
+    this.decoratedResource = decoratedResource;
+  }
+
+  @Override
+  protected void before() throws Throwable {
+    if (callCount == 0)
+      decoratedResource.before();
+    callCount++;
+  }
+
+  @Override
+  protected void after() {
+    callCount--;
+    if (callCount == 0)
+      decoratedResource.after();
+  }
+}
\ No newline at end of file