An IntegrationTest in Geode is a test involving inter-operation of components or subsystems.
- Should use JUnit 4 syntax
- File name ends with *IntegrationTest
- Should use Category annotation of type IntegrationTest
- May take longer than a UnitTest but should generally complete in seconds
- May test a single class or any combination of classes including end-to-end testing of Geode
- Typically uses black-box testing but may include some white-box testing and helps guarantee external quality (feature delivers value to User)
- An IntegrationTest may do any of the following:
- communicate across the network
- access the file system
- require anything special in the environment (such as editing config files or running an external process)
You would typically write an IntegrationTest if you want to test the interaction of real dependencies in a class or if you want to perform functional testing of any feature that requires more than a single class. Any testing that requires any kind of file access or networking should be done in an IntegrationTest instead of a UnitTest. Most of our IntegrationTests use either the file system or create an actual Cache for functional testing.
If you write an IntegrationTest to expose a bug and then confirm its fix, then you should name it with the suffix *RegressionTest. Otherwise you should use the suffix *IntegrationTest.
Preferred Testing Frameworks and Libraries for Geode IntegrationTests
Overall Framework
- JUnit 4 (including Rules)
- Paramerized – JUnit 4.12 has a bug which prevents Parameterized and Category from working together properly
- Please use CategoryWithParameterizedRunnerFactory from Geode when using Parameterized (see GEODE-1350)
- TemporaryFolder – provides isolated file system that will be cleaned up after the test
- TestName – provides name of the test method currently being tested
- ErrorCollector – use this when performing assertions in callbacks such as CacheListener
Assertion Library
- AssertJ – richer API with better failure messages than JUnit 4 Assert
Mocking Library
- Mockito
- PowerMock – use this as a last resort – refactoring code to facilitate better testing is preferred
Expected Exceptions
- Use AssertJ or Catch-Exception instead of JUnit 4 Rule ExpectedException
- AssertJ (assertThatThrownBy) – this is the cleanest and preferred way to test for expected exception
- Catch-Exception – better support for complex nesting of exceptions or exceptions with unusual APIs
Additional 3rd Party Libraries for UnitTests
- JUnitParams – provides test method parameterization (usually better to use than JUnit 4 Parameterized)
- System Rules – powerful set of JUnit 4 Rules
- RestoreSystemProperties
- Awaitility – useful for awaiting asynchronous conditions
Custom Geode JUnit 4 Rules and Testing Tools
- ExecutorServiceRule – provides ExecutorService for concurrent testing
- ExpectedTimeoutRule – verifies that an API times out and potentially throws TimeoutException
- JarFileRule – creates a Jar file in an internal TemporaryFolder containing a dynamically generated class
- RequiresGeodeHome – asserts precondition that GEODE_HOME system property is specified
- RestoreLocaleRule – restores JVM to original Locale
- RestoreTCCLRule – restores Thread Context Class Loader
- RetryRule – retries a test until it passes or exceeds specified number of retries
- TemporaryFileRule – destroys specified files or directories that aren't created in a TemporaryFolder
- UseJacksonForJsonPathRule – specifies that JsonPath should use Jackson instead of its default JSON library
- GfshRule – allows use of Gfsh script string to fork a JVM process in an IntegrationTest
Examples of actual tests
Examples of some good IntegrationTests:
RegionExpirationIntegrationTest
Examples of good RegressionTests which are IntegrationTests:
- StatArchiveWithMissingResourceTypeRegressionTest
General form of a IntegrationTest:
package org.apache.geode.internal.cache.backup; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.awaitility.Awaitility.await; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.experimental.categories.Category; import org.apache.geode.test.junit.categories.IntegrationTest; @Category(IntegrationTest.class) public class BackupIncrementalWithMissingBaselineRegressionTest { @Rule public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); ... @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Before public void setUp() { // arrange: create the SUT (system under test) and its dependencies // use JUnit 4 TemporaryFolder for any file system use } @Test public void incrementalBackupWithMissingBaselineThrows() { // act: attempt incremental backup with missing baseline directory // assert: verify thrown exception is not NullPointerException with correct message } }