Releases: sbabcoc/JUnit-Foundation
Remove orphan PhantomTimeout class
In this release, I deleted the PhantomTimeout class, which was part of my initial implementation for global rule-based test timeout management. During the course of developing this feature, I discovered that I didn't need this bit of byte-code magic, but I failed to remove the class for some reason. I re-discovered this class while searching for Byte Buddy code in need of revisions for compatibility with Java 11+.
Set Thread Context Class Loader when instantiating configuration
In this release, we added management of the Thread Context ClassLoader to the code that instantiates the JUnitConfig class as each thread calls for it. This avoids the failure that occurs if the Apache Commons Configuration classes are loaded by the standard application class loader, but configuration class instances get created by a different class loader (e.g. - PowerMock with its Javassist class loader). This class loader mismatch would cause failures like this:
org.apache.commons.configuration2.ex.ConfigurationRuntimeException: Incompatible result object: org.apache.commons.configuration2.PropertiesConfiguration@2f704ac9Projects that use PowerMock and JUnit Foundation with JVM versions newer than Java 8 will run into an additional issue with logging. This is a PowerMock issue that can be resolved by configuring PowerMock to ignore the affected packages:
@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "javax.management.*"})For more information:
Initialize configuration as early as possible
The first workaround I put in for the unexpected side-effects of PowerMock was sufficient to make my unit tests happy but failed to resolve the problem in real-world scenarios. This release moves the workaround to the earliest point I could find... the interception of the ParentRunner.run() method. This is well before the PowerMock runner gets started, which should avoid the configuration failure.
Upgrade dependencies; document test runner compatibility
In this release, I upgraded a few dependencies:
- Java-Utils:
1.9.3→2.0.0 - Settings:
2.3.3→2.3.5 - Guava:
28.1-android→30.1-android
I added information in README about compatibility with specific test runners, with special attention to how to use PowerMock successfully with JUnit Foundation.
I also eliminated the use of static imports with invocations of LifecycleHooks.getFieldValue(Object, String), as this really didn't add any value.
Add support for PowerMockRunner
This release works around an execution issue that prevented use of PowerMockRunner.
Once activated, PowerMock apparently wants to proxy everything. One side effect of this proclivity is that it can subtly alter objects in obscure parts of the implementation, far removed from any intended code. This can cause anomalous behavior, like the object-type mismatch that occurred deep in the bowels of Apache Commons Configuration.
To avoid the object-type mismatch, I now instantiate the configuration object in the interceptor for the run method, prior to the execution of PowerMockRunner.
These revisions include PowerMock unit tests. The JUnit Foundation library is targeted at Java 7, but Mockito requires Java 8, so I updated the project to compile for Java 7 and execute tests in Java 8. This effort was very useful, because figuring out how to get this to work revealed that I didn't need the maven-toolchain-plugin, the removal of which allowed me to eliminate the Eclipse lifecycle-mapping plugin.
NOTE - The native implementation of PowerMockRunner uses a deprecated JUnit runner model that JUnit Foundation doesn't support. You need to delegate test execution to the standard BlockJUnit4ClassRunner (or subclasses thereof) to enable reporting of test lifecycle events. This is specified via the @PowerMockRunnerDelegate annotation, as shown below:
package com.nordstrom.automation.junit;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.when;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(BlockJUnit4ClassRunner.class)
@PrepareForTest(PowerMockCases.StaticClass.class)
public class PowerMockCases {
@Test
public void testHappyPath() {
mockStatic(StaticClass.class);
when(StaticClass.staticMethod()).thenReturn("mocked");
assertThat(StaticClass.staticMethod(), equalTo("mocked"));
}
static class StaticClass {
public static String staticMethod() {
return null;
}
}
}
Add test lifecycle events for theory method permutations
This release expands on Theories runner support, adding test lifecycle event notifications for Theory method permutations. Without JUnit Foundation, these notifications are entirely absent - including AssumptionViolatedException events, which reveal data points that were rejected by @Theory methods as inapplicable.
Several chunks of code-generation magic were required to add these permutation events:
- The
methodBlockmethod is intercepted. For theory methods, the actual class runner statement is stored and a "lifecycle catalyst" statement is returned instead. This enables the interceptor declared in the RunWithCompleteAssignment class to manage the execution of the actual statement. - The
runWithCompleteAssignmentmethod is intercepted. The "lifecycle catalyst" created by the MethodBlock class attaches the class runner to the thread and creates a new atomic test for the target method. The actual method block statement is retrieved from MethodBlock and executed, publishing a complete set of test lifecycle events. - The
describeChildmethod is intercepted. For theory permutations, a unique ID is generated from the method name and arguments. This unique ID is injected into the description, allowing it to be used as a distinct "key" throughout the method lifecycle. - Tracking was added to the
runmethod interceptor to retain a mapping of parent runners to the notifiers that were provided to them. - Tracking was added to the RunAnnouncer listener to retain a mapping of method descriptions to their corresponding atomic test objects.
This release also includes various other theory-related revisions and optimizations of existing implementation. Share and enjoy!
Add support for the Theories runner
This new release adds support for the Theories runner. This runner enables you to implement "theory" methods (a parameterized equivalent of standard "test" methods) and have them executed against defined data points. Theory methods can define assumptions that will skip inapplicable data points.
NOTES ABOUT THE THEORIES RUNNER:
- The granularity of run notifications for theory methods is per method, not per iteration as would be the case with other parameterized runners. This can be worked around via the events published by the MethodWatcher facility.
- Iteration of defined data points terminates upon encountering any failure, even if there are data points that have yet to be tested against the current theory.
Upgrade JUnit version from 4.12 to 4.13.1
After years in stasis, the JUnit Team decided to push a batch of queued-up changed in a new release. I missed the first one (4.13), but I'm catching up to the latest here. Fortunately, it doesn't appear that any of the revisions were fundamental enough to break my crazy code generation hooks.
Consolidate attachment of RunListener service providers
In this release, I consolidated the attachment of standard JUnit RunListener service providers with the enhanced notification interfaces defined by JUnit Foundation. This enables implementers to declare all of their service providers in a single common configuration file, entirely eliminating redundancy. Note that per-interface configuration files are still supported for backward compatibility.
Add JUnitParams unit test and documentation
This release is essentially just wrapping up from the prior release. Support for the JUnitParams runner was deployed in version 12.1.0 of JUnit Foundation. The unit test will ensure that no one breaks this support without noticing, and the README documentation will help users figure out how to use this support to capture test method parameters.