Description
Description
No test cases are generated for methods involving Java Optional<T>
class. UtError
s are generated by the engine during traverse
due to ClassNotFound
exception: UtOptional
class can't be loaded. This behavior may be reproduced using the Idea plugin, but models are successfully generated for the same code in the engine unit tests.
To Reproduce
Generate unit tests for the sample method:
import java.util.Optional;
public class OptionalExamples {
public Optional<Integer> nonEmptyIfPositive(int n) {
if (n > 0) {
return Optional.of(n);
} else {
return Optional.empty();
}
}
}
Expected behavior
Two tests are generated, one for each branch in the code (see Additional context for more details).
Actual behavior
Errors are reported:
public class OptionalExamplesTest {
///region Test suites for executable collections.OptionalExamples.nonEmptyIfPositive
///region Errors report for nonEmptyIfPositive
public void testNonEmptyIfPositive_errors() {
// Couldn't generate some tests. List of errors:
//
// 2 occurrences of:
// org.utbot.engine.overrides.collections.UtOptional
}
///endregion
///endregion
}
Environment
Mockito: any configuration. Test framework: JUnit5.
Additional context
The primary error reason: UtOptional
wrapper is used instead of Optional
, but the class loader used by the test generator in the plugin can't find it. It's OK (engine-specific wrappers should never leak to the user), but shouldMock
function checks the class and fails with ClassNotFound
exception.
Adding a check to shouldMock
fixes the exception, but the execution path corresponding to Optional.empty()
return value is lost unless checkNpeForFinalFields
setting is set to true
(it is false
by default). See SootField.shouldBeNotNull()
declared in Extensions.kt
.
A similar code sample that uses OptionalInt
instead of Optional<Integer>
is processed without any problems, because primitive field types are never mocked so no class check is performed.
Code sample:
public OptionalInt nonEmptyOptionalIntIfPositive(int n) {
if (n > 0) {
return OptionalInt.of(n);
} else {
return OptionalInt.empty();
}
}
Generated tests:
///region SUCCESSFUL EXECUTIONS for method nonEmptyOptionalIntIfPositive(int)
/**
* <pre>
* Test executes conditions:
* {@code (n > 0): True }
* invokes:
* OptionalInt::of once
* returns from: {@code return OptionalInt.of(n); }
* </pre>
*/
@Test
@DisplayName("nonEmptyOptionalIntIfPositive: n > 0 : True -> return OptionalInt.of(n)")
public void testNonEmptyOptionalIntIfPositive_NGreaterThanZero() {
OptionalExamples optionalExamples = new OptionalExamples();
OptionalInt actual = optionalExamples.nonEmptyOptionalIntIfPositive(1);
OptionalInt expected = of(1);
assertTrue(deepEquals(expected, actual));
}
/**
* <pre>
* Test executes conditions:
* {@code (n > 0): False }
* </pre>
*/
@Test
@DisplayName("nonEmptyOptionalIntIfPositive: -> n > 0 : False")
public void testNonEmptyOptionalIntIfPositive_NLessOrEqualZero() throws ClassNotFoundException, Exception {
Class optionalIntClazz = Class.forName("java.util.OptionalInt");
OptionalInt prevEMPTY = ((OptionalInt) getStaticFieldValue(optionalIntClazz, "EMPTY"));
try {
OptionalInt empty = empty();
setStaticField(optionalIntClazz, "EMPTY", empty);
OptionalExamples optionalExamples = new OptionalExamples();
OptionalInt actual = optionalExamples.nonEmptyOptionalIntIfPositive(0);
assertTrue(deepEquals(empty, actual));
} finally {
setStaticField(OptionalInt.class, "EMPTY", prevEMPTY);
}
}
///endregion
Metadata
Metadata
Assignees
Labels
Type
Projects
Status