Parameterized Junit test on Android with NULL parameter - java

I am trying to write a JUNIT test with Parameterized test runner on android.
When i run it as part of AndroidTest in Android Studio it give me following error for first test:
ExampleTEst: Trying to set intVal with the value null that is not the
right type (String instead of Integer).
But when i run the same code as simple JUNIT test ( under android test in Android Studio) , it works fine.
Here is complete code of the example :
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.Collection;
import static junit.framework.Assert.assertEquals;
#RunWith(Parameterized.class)
public class ExampleTEst {
#Parameterized.Parameters
public static Collection<Object[]> params() {
return Arrays.asList(new Object[][]{
{//Test # 0 :
null,
"JOJO"
}
,
{//Test # 1 :
1,
null
}
});
}
#Parameterized.Parameter
public Integer intVal;
#Parameterized.Parameter(value = 1)
public String StringVal;
public static final Object[] resultsObjs = {null, "JOJO", 1, null};
public static int counter = 0;
#Test
public void aTest() {
assertEquals((Integer) resultsObjs[counter], intVal);
assertEquals((String) resultsObjs[counter + 1], StringVal);
counter += 2;
}
}

Related

Rerunning parametrized JUnit test with string array parameter fails

While using parametrized JUnit tests in Eclipse, I'm running into a problem when I want to rerun a single test. The tests themselves run fine, and while I can rerun the first test from the context menu, rerunning the second test:
fails with the following message:
java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=test[1: A2 --> [Ljava.lang.String;#1e4a7dd4]], {ExactMatcher:fDisplayName=test[1: A2 --> Ljava.lang.String;#1e4a7dd4]] from org.junit.internal.requests.ClassRequest#6c3f5566
I'm pretty sure this is because JUnit doesn't 'like' my arrays; for some context: I'm using this to account for the fact that due to external circumstances, the code under test can produce one of two outcomes for a particular test case.
Here is some code to reproduce this:
package com.stackexchange.toolbox;
import java.util.ArrayList;
import java.util.Arrays;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
#RunWith(Parameterized.class)
public class Tester {
public Tester(String source, Object target) {
this.source = source;
this.target = target;
}
private final String source;
private final Object target;
private static final Object[][] testCases = { { "A1", "B1" }, { "A2", new String[] { "B2", "C2" } } };
#Parameters(name = "{index}: {0} --> {1}")
public static Iterable<Object[]> data() throws Exception {
return new ArrayList<>(Arrays.asList(testCases));
}
#Test
public void test() throws Exception {
if (target instanceof String) {
Assert.assertEquals(source.charAt(1), ((String)target).charAt(1));
} else {
for (String target : (String[])this.target) {
Assert.assertEquals(source.charAt(1), target.charAt(1));
}
}
}
}
Is there an easy way to fix this, perhaps with Lists or variadic arguments? Most of the (100+) test cases are simple 'source', 'target' entries, and I'd like to keep the conciseness of { "A1", "B1" }.
This seems to be a limitation of JUnit4 (at least you get the same error on the command line).
The simplest and straightforward solution would be to migrate from JUnit4 to JUnit5, which would also mean less code:
package com.stackexchange.toolbox;
import java.util.Arrays;
import java.util.stream.Stream;
import org.junit.Assert;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
class Tester {
private static final Object[][] testCases = { { "A1", "B1" }, { "A2", new String[] { "B2", "C2" } } };
#ParameterizedTest(name = "{index}: {0} --> {1}")
#MethodSource("provideArguments")
void test(String source, Object target) {
if (target instanceof String) {
Assert.assertEquals(source.charAt(1), ((String)target).charAt(1));
} else {
for (String targetElement : (String[])target) {
Assert.assertEquals(source.charAt(1), targetElement.charAt(1));
}
}
}
static Stream<? extends Arguments> provideArguments() throws Exception {
return Arrays.stream(testCases).map(Arguments::of);
}
}

Parameterized junit test of custom validation in springboot

I have this validator in springboot which gives error when an integer is not between 1 and 3 and i am using addConstraintViolation to print a message from properties file
public class SizeValidator implements ConstraintValidator<SizeValidation, Integer> {
private int maxSize = 500;
private int minSize = 1;
#Override
public void initialize(SizeValidation constraintAnnotation) {
maxSize = constraintAnnotation.mxSize();
minSize = constraintAnnotation.minSize();
}
#Override
public boolean isValid(Integer givenSize, ConstraintValidatorContext context) {
if (givenSize > maxSize || givenSize<= minPageSize) {
addConstraintViolation(givenSize, "{Size.wrongSize.message}", context);
return false;
}
return true;
}
private void addConstraintViolation(Integer givenSize, String errorMessage, ConstraintValidatorContext context) {
final HibernateConstraintValidatorContext customContext = context.unwrap(HibernateConstraintValidatorContext.class);
customContext.addExpressionVariable("givenSize", givenSize);
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(errorMessage)
.addConstraintViolation();
}
}
and in my validation.properties
Size.wrongSize.message=Value of size ${givenSize} should be between 1
and 3
i wanted to write a Parameterized junit test for it as following but it returns nullpointerexception where am i doing wrong please?
java.lang.NullPointerException
at sizeValidator.addConstraintViolation(SizeValidator.java:33)
intellij says its at position
context.buildConstraintViolationWithTemplate(errorMessage)
.addConstraintViolation();
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.Collection;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
#RunWith(Enclosed.class)
public class SizeValidatorTest{
#RunWith(Parameterized.class)
public static class TestValidEntries {
private SizeValidator validator = new SizeValidator();
private Integer val;
public TestValidEntries(Integer val) {
super();
this.val = val;
}
#Test
public void test() {
assertTrue(isValid(val));
}
#Parameterized.Parameters(name = "{index} Valid: {0}")
public static Collection data() {
return Arrays.asList(
-1, 501
);
}
public boolean isValid(Integer value) {
final HibernateConstraintValidatorContext context = mock(HibernateConstraintValidatorContext.class);
when(context.unwrap(HibernateConstraintValidatorContext.class)).thenReturn(context);
when(context.addExpressionVariable(eq("nonUnique"), anyString())).thenReturn(context);
when(context.getDefaultConstraintMessageTemplate()).thenReturn("template");
final ConstraintValidatorContext.ConstraintViolationBuilder builder = mock(ConstraintValidatorContext.ConstraintViolationBuilder.class);
when(context.buildConstraintViolationWithTemplate("template")).thenReturn(builder);
when(builder.addPropertyNode(anyString())).thenReturn(mock(ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext.class));
return validator.isValid(value, context);
}
}
}
The problem is in the configuration of the context mock.
Your configuration says:
when(context
.buildConstraintViolationWithTemplate(
"template"))
.thenReturn(builder);
But the string you pass in your code under test is: "{Size.wrongSize.message}"
Because this string do not match the mock returns null.
A better approach is to always use unspecific matchers like anyString() in the arrange part of the test method (or in setup) an specific matchers (or values) only in conjunction with Mockito.verify() in the assert part of the test method.

Pass a parameter to dataProvide when using the #Factory TestNG

There is an exception Using TestNG's #Factory and #dataProvider annotations it is not possible to pass the calling test name, this needed when building a generic test as a framework to provide different data each time(from Excel). Using Method getName() at the Dataprovider cause run time Exception. The getName() function is working when using #dataprovider only. However combined with #Factory the exception occurs. Is there a ways to solve or bypass this issue?
package Tests;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.testng.ITestContext;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public abstract class GenericFactory11 {
protected List<String> data;
public GenericFactory11(List<String> data) {
this.data = data;
}
#DataProvider(name = "getDataForInstances")
public static Object[][] getDataForInstances(ITestContext context,Method m){
System.out.println(context.getName());
System.out.println(m.getName()); // THIS Line Causes the exception
return new Object[][]{
{Collections.singletonList("Java")},
{Arrays.asList("TestNG", "JUnit")},
{Arrays.asList("Maven", "Gradle", "Ant")}
};
}
}
package Tests;
import static org.testng.Assert.assertNotEquals;
import java.util.List;
import org.testng.annotations.Factory;
import org.testng.annotations.Test;
public class Sanity11 extends GenericFactory11 {
#Factory (dataProvider = "getDataForInstances")
public Sanity11(List<String> data) {
super(data);
}
#Test
public void Sanity(){
String text = this.data.get(this.data.size()-1);
System.out.println("Printing Parameters when running test method [" + text + "]");
assertNotEquals(text,"");
}
}
Running the code the following error is received:
java.lang.RuntimeException: java.lang.NullPointerException
at org.testng.internal.MethodInvocationHelper.invokeMethodNoCheckedException(MethodInvocationHelper.java:49)
You are seeing a NullPointerException because your data provider states it would be accepting a java.lang.reflect.Method object, but in this case, the calling method is a java.lang.reflect.Constructor and not a Method object.
You should be replacing java.lang.reflect.Method with org.testng.ITestNGMethod.
Here's how your modified data provider looks like:
#DataProvider(name = "getDataForInstances")
public static Object[][] getDataForInstances(ITestContext context, ITestNGMethod method) {
System.out.println("test name = " + context.getName());
System.out.println("Method name = " + method.getConstructorOrMethod().getName() + "()\n");
return new Object[][] {
{Collections.singletonList("Java")},
{Arrays.asList("TestNG", "JUnit")},
{Arrays.asList("Maven", "Gradle", "Ant")}
};
}

iterative testing using junit

I have a bunch of test inputs that I would like to run and compare the output with expected:
#Test
void test () throws IOExeption {
for (File i : readDir()) {
File out = foo(i);
assertEquals(FileUtils.readLines(expected), FileUtils.readLines(out));
}
}
I would like to run the tests using JUnit. But if I do it like the above then JUnit will stop after encountering the first test failure. Is there a better way to do this other than making each file its own test case like below?
#Test
void test1 () throws IOExeption {
File i = readFile("1.txt");
File out = foo(i);
assertEquals(FileUtils.readLines(expected), FileUtils.readLines(out));
}
#Test
void test2 () throws IOExeption {
File i = readFile("2.txt");
File out = foo(i);
assertEquals(FileUtils.readLines(expected), FileUtils.readLines(out));
}
I think, that you could use Parameterized. This is standard feature of JUnit. Below you can see an example.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.Collection;
#RunWith(Parameterized.class)
public class Foo {
#Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] { { "1.txt" }, { "2.txt" } });
}
#Parameterized.Parameter // first data value (0) is default
public /* NOT private */ String fileName;
#Test
public void test() {
File i = readFile(fileName);
File out = foo(i);
assertEquals(FileUtils.readLines(expected), FileUtils.readLines(out));
}
}
I have not found JUnit official documentation about this, but you can find more details e.g. in this tutorial: https://www.tutorialspoint.com/junit/junit_parameterized_test.htm

Passing arrays to Parameterized JUnit

I am new to parameterized feature of JUnit 4.x and having a problem. My parameterized test consists of 3 integer arrays and I am having difficulty of how to declare them. What I have below generates run-time error:
testGeneral[0] caused an ERROR: argument type mismatch
argument type mismatch
java.lang.IllegalArgumentException
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
testGeneral[1] caused an ERROR: argument type mismatch
argument type mismatch
java.lang.IllegalArgumentException
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
Here is my code:
#RunWith(Parameterized.class)
public class MyArrayTest {
private Integer[] inputList1;
private Integer[] inputList2;
private Integer[] expectedList;
public MyArrayTest(Integer[] li1, Integer[] li2, Integer[] expected) {
// ========> Runtime error happens here. <=========
this.inputList1 = li1;
this.inputList2 = li2;
this.expectedList = expected;
}
#Parameterized.Parameters
public static Collection testCases() {
return Arrays.asList(new Object[][][] {
{{1,1,1}, {2,2,2}, {3,3,3}},
{{2,2,2}, {3,3,3}, {4,4,4}}
});
}
#Test
public void testGeneral() {
// Do some test with this.inputList1, this.inputList2,
// and verify with this.expectedList
// I am not even getting here yet.
}
}
I appreciate your help to correctly passing the three arrays to my tests.
The reason why it is failing is because your test expects Integer arrays whereas you are passing Object type. So you are expanding the type. Try this:
#Parameterized.Parameters
public static Collection testCases() {
return Arrays.asList(new Integer[][][] {
{{1,1,1}, {2,2,2}, {3,3,3}},
{{2,2,2}, {3,3,3}, {4,4,4}}
});
}
This solution uses junitparams, implements junitparams.converters.Converter and parses list of long values as parameters.
package example;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import junitparams.converters.ConversionFailedException;
import junitparams.converters.Converter;
import junitparams.converters.Param;
#RunWith(JUnitParamsRunner.class)
public class LongArrayParameterTest {
#Parameters({ "0|10", "1|10;20;30" })
#Test
public void test(final long otherParameter, #LongArrayParam final long[] expected) {
System.out.println(Arrays.stream(expected).boxed().map(l -> Long.toString(l)).collect(Collectors.toList()));
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
#Param(converter = LongArrayConverter.class)
public #interface LongArrayParam {
}
public static class LongArrayConverter implements Converter<LongArrayParam, long[]> {
#Override
public void initialize(final LongArrayParam annotation) {
}
#Override
public long[] convert(final Object param) throws ConversionFailedException {
final String str = (String) param;
final String[] longStrings = str.split(";");
return Arrays.stream(longStrings).mapToLong(s -> Long.parseLong(s)).toArray();
}
}
}
This parser does not support empty list.

Categories