JUnit 5 Parameterized test #ArgumentsSource parameters not loading - testing

I have created below JUnit5 parameterized test with ArgumentsSource for loading arguments for the test:
public class DemoModelValidationTest {
public ParamsProvider paramsProvider;
public DemoModelValidationTest () {
try {
paramsProvider = new ParamsProvider();
}
catch (Exception iaex) {
}
}
#ParameterizedTest
#ArgumentsSource(ParamsProvider.class)
void testAllConfigurations(int configIndex, String a) throws Exception {
paramsProvider.executeSimulation(configIndex);
}
}
and the ParamsProvider class looks like below:
public class ParamsProvider implements ArgumentsProvider {
public static final String modelPath = System.getProperty("user.dir") + File.separator + "demoModels";
YAMLDeserializer deserializedYAML;
MetaModelToValidationModel converter;
ValidationRunner runner;
List<Configuration> configurationList;
List<Arguments> listOfArguments;
public ParamsProvider() throws Exception {
configurationList = new ArrayList<>();
listOfArguments = new LinkedList<>();
deserializedYAML = new YAMLDeserializer(modelPath);
deserializedYAML.load();
converter = new MetaModelToValidationModel(deserializedYAML);
runner = converter.convert();
configurationList = runner.getConfigurations();
for (int i = 0; i < configurationList.size(); i++) {
listOfArguments.add(Arguments.of(i, configurationList.get(i).getName()));
}
}
public void executeSimulation(int configListIndex) throws Exception {
final Configuration config = runner.getConfigurations().get(configListIndex);
runner.run(config);
runner.getReporter().consolePrintReport();
}
#Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return listOfArguments.stream().map(Arguments::of);
// return Stream.of(Arguments.of(0, "Actuator Power"), Arguments.of(1, "Error Logging"));
}}
In the provideArguments() method, the commented out code is working fine, but the first line of code
listOfArguments.stream().map(Arguments::of)
is returning the following error:
org.junit.platform.commons.PreconditionViolationException: Configuration error: You must configure at least one set of arguments for this #ParameterizedTest
I am not sure whether I am having a casting problem for the stream in provideArguments() method, but I guess it somehow cannot map the elements of listOfArguments to the stream, which can finally take the form like below:
Stream.of(Arguments.of(0, "Actuator Power"), Arguments.of(1, "Error Logging"))
Am I missing a proper stream mapping of listOfArguments?

provideArguments(…) is called before your test is invoked.
Your ParamsProvider class is instantiated by JUnit. Whatever you’re doing in desiralizeAndCreateValidationRunnerInstance should be done in the ParamsProvider constructor.
Also you’re already wrapping the values fro deserialised configurations to Arguments and you’re double wrapping them in providesArguments.
Do this:
#Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return listOfArguments.stream();
}}

Related

JUnit5 - how to pass input collection to ParameterizedTest

I'm trying to translate a ParameterizedTest from JUnit4 to JUnit5 (sadly I'm not particularly skilled in testing).
In JUnit4 I have the following class:
#RunWith(Parameterized.class)
public class AssertionTestCase {
private final TestInput testInput;
public AssertionTestCase(TestInput testInput) {
this.testInput = testInput;
}
#Parameterized.Parameters
public static Collection<Object[]> data() {
return AssertionTestCaseDataProvider.createDataCase();
}
#Test(timeout = 15 * 60 * 1000L)
public void testDailyAssertion() {
LOG.info("Testing input {}/{}", testInput.getTestCase(), testInput.getTestName());
//assert stuffs
}
}
in the AssertionTestCaseDataProvider class I have a simple method generating a collection of Object[]:
class AssertionTestCaseDataProvider {
static Collection<Object[]> createDataCase() {
final List<TestInput> testInputs = new ArrayList<>();
//create and populate testInputs
return testInputs.stream()
.map(testInput -> new Object[]{testInput})
.collect(Collectors.toList());
}
}
I've been trying to translate it using JUnit5 and obtained this:
class AssertionTestCase {
private final TestInput testInput;
public AssertionTestCase(TestInput testInput) {
this.testInput = testInput;
}
public static Collection<Object[]> data() {
return AssertionTestCaseDataProvider.createDataCase();
}
#ParameterizedTest
#MethodSource("data")
void testDailyAssertion() {
LOG.info("Testing input {}/{}", testInput.getTestCase(), testInput.getTestName());
// assert stuffs
}
}
I did not apply any change to the AssertionTestCaseDataProvider class.
Nevertheless, I'm getting the following error:
No ParameterResolver registered for parameter [com.xxx.xx.xxx.xxx.testinput.TestInput arg0] in constructor [public `com.xxx.xxx.xxx.xxx.xxx.AssertionTestCase(com.xxx.xxx.xxx.xxx.testinput.TestInput)]. org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [com.xxx.xx.xxx.xxx.testinput.TestInput arg0] in constructor [public com.xxx.xxx.xxx.xxx.xxx.AssertionTestCase(com.xxx.xxx.xxx.xxx.testinput.TestInput)].`
I understand I'm probably not applying correctly JUnit5 when initializing the input collection for the test. Am I missing some annotations?
I've also tried to use #ArgumentSource instead of #MethodSource and implementing Argument for AssertionTestCaseDataProvider, with the same failing results.
It works in a bit another way in Junit5.
Test Method should have parameters, and provider method should return a Stream.
static Stream<Arguments> data(){
return Stream.of(
Arguments.of("a", 1),
Arguments.of("d", 2)
);
}
#ParameterizedTest
#MethodSource("data")
void testDailyAssertion(String a, int b) {
Assertions.assertAll(
() -> Assertions.assertEquals("a", a),
() -> Assertions.assertEquals(1, b)
);
}
In your case you can just return a Stream<TestInput>:
static Stream<TestInput> createDataCase() {
final List<TestInput> testInputs = new ArrayList<>();
//create and populate testInputs
return testInputs.stream();
}
and then in your testMethod:
#ParameterizedTest
#MethodSource("createDataCase")
void testDailyAssertion(TestInput testInput) {
{your assertions}
}

Customized parameter logging when using aspect oriented programing

All the examples I've seen that use aspect oriented programming for logging either log just class, method name and duration, and if they log parameters and return values they simply use ToString(). I need to have more control over what is logged. For example I want to skip passwords, or in some cases log all properties of an object but in other cases just the id property.
Any suggestions? I looked at AspectJ in Java and Unity interception in C# and could not find a solution.
You could try introducing parameter annotations to augment your parameters with some attributes. One of those attributes could signal to skip logging the parameter, another one could be used to specify a converter class for the string representation.
With the following annotations:
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Log {
}
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface SkipLogging {
}
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface ToStringWith {
Class<? extends Function<?, String>> value();
}
the aspect could look like this:
import java.lang.reflect.Parameter;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public aspect LoggingAspect {
private final static Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
pointcut loggableMethod(): execution(#Log * *..*.*(..));
before(): loggableMethod() {
MethodSignature signature = (MethodSignature) thisJoinPoint.getSignature();
Parameter[] parameters = signature.getMethod()
.getParameters();
String message = IntStream.range(0, parameters.length)
.filter(i -> this.isLoggable(parameters[i]))
.<String>mapToObj(i -> toString(parameters[i], thisJoinPoint.getArgs()[i]))
.collect(Collectors.joining(", ",
"method execution " + signature.getName() + "(", ")"));
Logger methodLogger = LoggerFactory.getLogger(
thisJoinPointStaticPart.getSignature().getDeclaringType());
methodLogger.debug(message);
}
private boolean isLoggable(Parameter parameter) {
return parameter.getAnnotation(SkipLogging.class) == null;
}
private String toString(Parameter parameter, Object value) {
ToStringWith toStringWith = parameter.getAnnotation(ToStringWith.class);
if (toStringWith != null) {
Class<? extends Function<?, String>> converterClass =
toStringWith.value();
try {
#SuppressWarnings("unchecked")
Function<Object, String> converter = (Function<Object, String>)
converterClass.newInstance();
String str = converter.apply(value);
return String.format("%s='%s'", parameter.getName(), str);
} catch (Exception e) {
logger.error("Couldn't instantiate toString converter for logging "
+ converterClass.getName(), e);
return String.format("%s=<error converting to string>",
parameter.getName());
}
} else {
return String.format("%s='%s'", parameter.getName(), String.valueOf(value));
}
}
}
Test code:
public static class SomethingToStringConverter implements Function<Something, String> {
#Override
public String apply(Something something) {
return "Something nice";
}
}
#Log
public void test(
#ToStringWith(SomethingToStringConverter.class) Something something,
String string,
#SkipLogging Class<?> cls,
Object object) {
}
public static void main(String[] args) {
// execution of this method should log the following message:
// method execution test(something='Something nice', string='some string', object='null')
test(new Something(), "some string", Object.class, null);
}
I used Java 8 Streams API in my answer for it's compactness, you could convert the code to normal Java code if you don't use Java 8 features or need better efficiency. It's just to give you an idea.

How to generalize a JMockit test using Spring autowiring

So I would like to use a generic test for a few different Dao methods. Inside the Dao, I implemented the save functionality to be Entity independent, so I figured it would be best to make the tests Entity independent as well. Currently I have the following for one of my jmockit tests that is autowired with spring.
#Injectable
public EntityManager em;
#Tested
SyncClaimDao syncClaimDao = new SyncClaimDaoImpl();
#Before
public void setUp() {
Deencapsulation.setField(syncClaimDao, "em", em);
}
private void testSaveEntity (Class T) {
// Existing claim happy path
new Expectations() {
{
em.contains(any); result = true;
em.merge(any);
}
};
if (T.isInstance(SyncClaimEntity.class)) {
Assert.assertTrue(syncClaimDao.saveClaim(new SyncClaimEntity()));
} else if (...) {...}
}
#Test
public void testSaveClaim() {
testSaveEntity(SyncClaimEntity.class);
}
SyncClaimDaoImpl
#Override
public boolean saveClaim(SyncClaimEntity claim) {
return saveEntity(claim);
}
private boolean saveEntity(Object entity) {
boolean isPersisted = false;
try {
isPersisted = em.contains(entity);
if (isPersisted) {
em.merge(entity);
} else {
em.persist(entity);
em.flush();
isPersisted = true;
}
logger.debug("Persisting " + entity.getClass().getSimpleName() + ": " + entity.toString());
}
catch (NullPointerException ex) {
...
}
catch (IllegalArgumentException ex) {
...
}
return isPersisted;
}
When I run the tests I am seeing the following errors:
mockit.internal.MissingInvocation: Missing invocation of:
javax.persistence.EntityManager#contains(Object)
with arguments: any Object
on mock instance: javax.persistence.$Impl_EntityManager#44022631
at at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
... 4 more
Caused by: Missing invocation
at [redacted].dal.dao.SyncClaimDaoImplTest$1.<init>(SyncClaimDaoImplTest.java:48)
at [redacted].dal.dao.SyncClaimDaoImplTest.testSaveEntity(SyncClaimDaoImplTest.java:46)
at [redacted].dal.dao.SyncClaimDaoImplTest.testSaveClaim(SyncClaimDaoImplTest.java:67)
... 10 more
Now if I just move the Expectations block into the #Test method like so:
#Test
public void testSaveClaim() {
new Expectations() {
{
em.contains(any); result = true;
em.merge(any);
}
};
Assert.assertTrue(syncClaimDao.saveClaim(new SyncClaimEntity()));
I get a successful test run as should be. I'm thinking that the spring autowiring for the Test method is not properly scoping my Expectations. That's why I'm seeing the missing invocation errors.
Does anyone have any ideas on how to generalize my Expectations so I can create simpler tests for generalized methods?
I see the mistake now: T.isInstance(SyncClaimEntity.class). The Class#isInstance(Object) method is supposed to be called with an instance of the class, not with the class itself; so, it's always returning false because SyncClaimEntity.class is obviously not an instance of SyncClaimEntity.

AspectJ, Intertype Definition

I am receiving this error when I compile
The type XXX must implement the inherited abstract method
I have three files
A default implementation [com.SafeReaderIMPL.java]
public class SafeReaderIMPL implements ISafeReader {
private boolean successfulRead;
public SafeReaderIMPL() {
successfulRead = true;
}
protected void fail() {
successfulRead = false;
}
#Override
public boolean isSuccessfulRead() {
return successfulRead;
}
}
An interface file [com.ISafeReader.java]
public interface ISafeReader {
public boolean isSuccessfulRead();
}
An apsect (using annotations) [com.SafeReaderAspect.java]
#Aspect
public class SafeReaderAspect {
#DeclareParents(value = "com.BadReader", defaultImpl = SafeReaderIMPL.class)
public ISafeReader implementedInterface;
#AfterThrowing(pointcut = "execution(* *.*(..)) && this(m)", throwing = "e")
public void handleBadRead(JoinPoint joinPoint, ISafeReader m, Throwable e) {
((SafeReaderIMPL)m).fail();
}
}
And a Test Class [com.BadReader]
public class BadReader {
public void fail() throws Throwable {
throw new Throwable();
}
}
I compile the first three files in a separate jar using
ajc -source 1.8 -sourceroots . -outjar aspectLib.jar
I then compile the second file using the aspectLib like so
ajc -source 1.8 -sourceroots . -aspectpath ./aspectLib.jar -outjar common.jar
When I go to compile the second jar I get the error. I am using the latest stable version of AspectJ 1.8.3
BadReader.java:10 [error] The type BadReader must implement the
inherited abstract method ISafeReader.isSuccessfulRead() public class
BadReader {
^^^^^^^^
The problem is not two-step compilation as such, but the fact that #DeclareParents in #AspectJ syntax in not 100% compatible with declare parents in native syntax. Actually, #DeclareParents for introducing default interface implementations is superseded by #DeclareMixin (see this bug ticket), but the downside of the mixin approach is that you do not have a real A implements B scenario there, i.e. you cannot cast as you wish in your after-throwing advice, so this is also not a good option in your case.
So what do you do if you want to keep the two-step compilation approach? Just use native syntax:
Interface:
package com;
public interface ISafeReader {
boolean isSuccessfulRead();
}
Default implementation:
package com;
public class SafeReaderIMPL implements ISafeReader {
private boolean successfulRead;
public SafeReaderIMPL() { successfulRead = true; }
public void fail() { successfulRead = false; }
#Override public boolean isSuccessfulRead() { return successfulRead; }
}
ITD aspect:
package com;
public aspect SafeReaderAspect {
declare parents : com.BadReader implements SafeReaderIMPL;
after(ISafeReader safeReader) throwing : execution(* *(..)) && this(safeReader) {
System.out.println(thisJoinPoint + " - calling 'fail()' before rethrowing error");
((SafeReaderIMPL) safeReader).fail();
}
}
ITD target class with sample main method:
package com;
public class BadReader {
public void doSomething() {
throw new RuntimeException("my error");
}
public static void main(String[] args) {
BadReader badReader = new BadReader();
System.out.println("badReader.isSuccessfulRead() = " + badReader.isSuccessfulRead());
try { badReader.doSomething(); }
catch(Throwable t) { System.out.println(t); }
System.out.println("badReader.isSuccessfulRead() = " + badReader.isSuccessfulRead());
}
}
Now you can use the two-stage compilation approach.
Console output:
badReader.isSuccessfulRead() = true
execution(void com.BadReader.doSomething()) - calling 'fail()' before rethrowing error
java.lang.RuntimeException: my error
badReader.isSuccessfulRead() = false
The problem is due to the two-step compilation. During the second step, ajc needs the source code of SafeReaderIMPL to be able to weave BadReader, but it cannot find it into aspectLib.jar
In fact, if you try compiling in a single step (I did), it compiles and runs.
Unfortunately I don't know a way to fix this without providing the source code during the second compile step, which I suppose would render the whole two-step approach a bit pointless.

TestNG Test Case failing with JMockit "Invalid context for the recording of expectations"

The following TestNG (6.3) test case generates the error "Invalid context for the recording of expectations"
#Listeners({ Initializer.class })
public final class ClassUnderTestTest {
private ClassUnderTest cut;
#SuppressWarnings("unused")
#BeforeMethod
private void initialise() {
cut = new ClassUnderTest();
}
#Test
public void doSomething() {
new Expectations() {
MockedClass tmc;
{
tmc.doMethod("Hello"); result = "Hello";
}
};
String result = cut.doSomething();
assertEquals(result, "Hello");
}
}
The class under test is below.
public class ClassUnderTest {
MockedClass service = new MockedClass();
MockedInterface ifce = new MockedInterfaceImpl();
public String doSomething() {
return (String) service.doMethod("Hello");
}
public String doSomethingElse() {
return (String) ifce.testMethod("Hello again");
}
}
I am making the assumption that because I am using the #Listeners annotation that I do not require the javaagent command line argument. This assumption may be wrong....
Can anyone point out what I have missed?
The JMockit-TestNG Initializer must run once for the whole test run, so using #Listeners on individual test classes won't work.
Instead, simply upgrade to JMockit 0.999.11, which works transparently with TestNG 6.2+, without any need to specify a listener or the -javaagent parameter (unless running on JDK 1.5).