A UnitTest in Geode is a test with very narrow and well defined scope. Any complex dependencies and interactions are stubbed or mocked.
- Should use JUnit 4 or JUnit Jupiter (aka JUnit 5) syntax
- File name ends with *Test
- Should use Category annotation of type UnitTestbe part of the test source set folder (<geode-module-dir>/test/java)
- Should complete in milliseconds
- Should generally test a single class
- Typically uses white-box testing, uses Mocks/Fakes and helps guarantee internal quality (quality of code and class design)
- Follows "A Set of Unit Testing Rules" by Michael Feathers
- A UnitTest should not do any of the following:
- communicate with a database
- communicate across the network
- access the file system
- prevent the running of other unit tests in parallel
- require anything special in the environment (such as editing config files or running an external process)
- A UnitTest should not do any of the following:
...
It's not absolutely necessary to introduce a new *RegressionTest class for covering a bug. If new regression test coverage fits better within an existing test class, then add one or more new tests to that existing test class but include javadocs on the new test methods which includes the GEODE JIRA ticket number and its summary at a minimum.
Preferred Testing Frameworks and Libraries Appropriate for Geode UnitTests
Overall Framework
- JUnit 4 (including Rules) or JUnit Jupiter (which does not support 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)
Assertion Library
- AssertJ – richer API with better failure messages than JUnit 4 Assert
Mocking Library
- MockitoPowerMock – 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
- Awaitility* – useful for awaiting asynchronous conditions
(*=Concurrency frameworks or rules are typically more appropriate for IntegrationTests)
Custom Geode JUnit 4 Rules and Testing Tools
- RestoreLocaleRule – restores JVM to original Locale
- RestoreTCCLRule – restores Thread Context Class Loader
- ExecutorServiceRule* – provides ExecutorService for concurrent testing
(*=Concurrency frameworks or rules are typically more appropriate for IntegrationTests)
Examples of actual tests
Examples of some good UnitTests:
...
Code Block |
---|
package org.apache.geode.internal.cache.control;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.apache.geode.test.junit.categories.UnitTest;
@Category(UnitTest.class)
public class RebalanceTaskTest {
private RebalanceTask rebalanceTask;
private RebalanceStats rebalanceStats;
@Before
public void setUp() {
// arrange: create the SUT (system under test) and its dependencies
rebalanceStats = mock(RebalanceStats.class);
rebalanceTask = new RebalanceTask(rebalanceStats);
}
@After
public void tearDown() {
// cleanup anything that might affect a later test or that uses any resources
}
@Test
public void execIncrementsRebalanceStats() throws Exception {
// act: do something like invoke exec
// assert: assertThat expected behavior or interaction with dependency occurred
}
} |
...