-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Closed
Description
Concurrency issues with resolving parameters in test methods
Steps to reproduce
- Gradle java project with Junit5 tests that use
SpringExtension
- Have at least one method
@Test/@BeforeEach
with parameter annotated with spring@Autowired
- Run all test with enabled default parallel strategy
junit.jupiter.execution.parallel.enabled= true
- And with default "per-method" test instance lifecycle
Expected behavior
- Test passed
Actual behavior
- Some times get concurrency issues for example
Caused by: java.lang.IllegalArgumentException:
Given parameter [com.example.project.Calculator arg0]
does not match any parameter in the declaring executable
Example project
- That reproduce issue https://github.com/mdolinin/gradle-junit5-spring-example
Context
- Used versions : Jupiter 5.3.1 /Platform 1.3.1/ Spring Test 5.1.2
- Build Tool/IDE: gradle 4.10.2
Posible rootcause
- After some investigation found that this exceptions is thrown by Spring from
org.springframework.core.MethodParameter
protected static int findParameterIndex(Parameter parameter) {
Executable executable = parameter.getDeclaringExecutable();
Parameter[] allParams = executable.getParameters();
for (int i = 0; i < allParams.length; i++) {
if (parameter == allParams[i]) {
return i;
}
}
throw new IllegalArgumentException("Given parameter [" + parameter +
"] does not match any parameter in the declaring executable");
}
- This could happen because how JUnit create this parameter in
https://github.com/junit-team/junit5/blob/5caf217676e85a51aabec736fce7cadf687a299d/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ExecutableInvoker.java#L160 - If run this in parallel two or more thread will get here with same link to one
executable
method - And because inside
Executable
class parameters are stored inprivate transient volatile Parameter[] parameters;
private Parameter[] privateGetParameters() {
// Use tmp to avoid multiple writes to a volatile.
Parameter[] tmp = parameters;
if (tmp == null) {
// Otherwise, go to the JVM to get them
try {
tmp = getParameters0();
} catch(IllegalArgumentException e) {
// Rethrow ClassFormatErrors
throw new MalformedParametersException("Invalid constant pool index");
}
// If we get back nothing, then synthesize parameters
if (tmp == null) {
hasRealParameterData = false;
tmp = synthesizeAllParams();
} else {
hasRealParameterData = true;
verifyParameters(tmp);
}
parameters = tmp;
}
return tmp;
}
- First thread could get parameters array from second one
Posible solutions
- Synchronize calls to
executable
- Or provide clone of
executable
to each thread
mvershinin-chwy and vtulla-chwy