Releases: sbabcoc/JUnit-Foundation
Resolve issue acquiring runner in threaded execution context
If the JUnit framework detects that a timeout rule is in effect, new time-limited threads are spawned to execute test methods. In this context, the "thread runner" is initially unset when RunReflectiveCall is invoked. This release adds logic to store the association between test class instances and the runners that created them, and these associations provide the runner objects for these new threads.
Fix event sequencing issues
The previous release contained a bug that caused runStarted
events to be published after the invocation events for @BeforeClass
methods. There were also object management issues that may have caused erratic behavior when tests were run in parallel.
Release framework object ASAP to reduce memory consumption
This release resolves memory consumption issues caused by the retention of framework objects collected during the course of test execution. While implementing these revisions, I discovered and resolved a few fundamental issues in the handling of parameterized tests as well.
Resolving these fundamental issues required me to revise a few public interfaces, which informed the major-version bump.
- The AtomicTest class is no longer parameterized.
- The RunWatcher interface is no longer parameterized.
- The LifecycleHooks class now provides
target => atomic test
mappings. - The LifecycleHooks class now provides
description => atomic test
mappings. - The LifecycleHooks class no longer publishes
runner <=> target
mappings. - The signature of the TestObjectWatcher.testObjectCreated() interface method has changed.
Add JavaDoc to the configuration settings; deprecate the RuleChainWalker
This release is focused on resolving a couple of minor issues, both related to a lack of documentation.
- I added JavaDoc to the constants of the JUnitConfig class to resolve #85.
- I deprecated the RuleChainWalker class to resolve #84.
The latter change was informed by the fact that the @rule annotation now includes an order
parameter that provides the same sort of invocation order control that the RuleChain class gives.
Release references to out-of-scope JUnit framework objects
In this release, references to JUnit framework objects are released when these objects pass out-of-scope. This should resolve excessive memory utilization problems associated with large test suites.
Previously, references were retained for the lifetime of the Java instance that hosted test execution. If the test collection was relatively modest, this didn't constitute a significant issue. However, the accumulation of objects during the execution of large suites of tests could result in out-of-memory failures.
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@2f704ac9
Projects 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;
}
}
}