Parameterized junit test of custom validation in springboot - java

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.

Related

Mokito/Java - Static Methods Mock

For example I have the following classes below:
public class TesteEstatico {
public static String teste(){
return "FOO";
}
}
And I have a class that uses her method:
public class UsaTesteEstatico {
public String metodoParaTeste1 (){
return TesteEstatico.teste() + " BAR ";
}
public String metodoParaTeste2 (){
return "FOO "+TesteEstatico.teste() + " BAR ";
}
}
Test class:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
#ExtendWith(MockitoExtension.class) public class UsaTesteEstaticoTest {
#InjectMocks
UsaTesteEstatico usaTesteEstatico;
#Test
void teste1(){
Mockito.mockStatic(TesteEstatico.class);
Mockito.when(TesteEstatico.teste())
.thenReturn("BANANA");
String res = usaTesteEstatico.metodoParaTeste1();
System.out.println(res);
}
#Test
void teste2(){
Mockito.mockStatic(TesteEstatico.class);
Mockito.when(TesteEstatico.teste())
.thenReturn("LARANJA");
String res = usaTesteEstatico.metodoParaTeste2();
System.out.println(res);
}
}
Error I get when trying to run the tests:
org.mockito.exceptions.base.MockitoException:
For TesteEstatico, static mocking is already registered in the current thread
To create a new mock, the existing static mock registration must be deregistered
Versions of the libs that are in the project:
junit-jupiter 5.5.2
mockito-junit-jupiter 3.2.14
mockito-inline 3.2.14
Any idea how to solve this, i've tried a few things but nothing successful.
NOTE: I cannot change or add any new libraries as it is a restricted project.
You should use try-with-resources block in each of the tests to close the mockStatic.
public class UsaTesteEstaticoTest {
UsaTesteEstatico usaTesteEstatico = new UsaTesteEstatico();
#Test
void teste1(){
try (var ms = Mockito.mockStatic(TesteEstatico.class)) {
Mockito.when(TesteEstatico.teste()).thenReturn("BANANA");
String res = usaTesteEstatico.metodoParaTeste1();
System.out.println(res);
}
}
#Test
void teste2(){
try (var ms = Mockito.mockStatic(TesteEstatico.class)) {
Mockito.when(TesteEstatico.teste()).thenReturn("LARANJA");
String res = usaTesteEstatico.metodoParaTeste2();
System.out.println(res);
}
}
}
Note on mockStatic in #BeforeAll
Using #BeforeAll is a trap and bad advice.
You should strive for independent tests that don't affect each other.
This is not the case for mockStatic called in #BeforeAll, as stubbing from test methods outlive the test methods.
For example
// BAD CODE DONT USE
public class UsaTesteEstaticoTest {
UsaTesteEstatico usaTesteEstatico = new UsaTesteEstatico();
static MockedStatic<TesteEstatico> ms;
#BeforeAll
public static void init() {
ms = Mockito.mockStatic(TesteEstatico.class);
}
#AfterAll
public static void close() {
ms.close();
}
#Test
void teste1() {
Mockito.when(TesteEstatico.teste()).thenReturn("BANANA");
String res = usaTesteEstatico.metodoParaTeste1();
System.out.println(res);
}
#Test
void teste2() {
String res = usaTesteEstatico.metodoParaTeste2();
System.out.println(res);
}
}
teste2 prints:
FOO BANANA BAR if run after teste1
FOO null BAR if run separately
This is precisely what you want to avoid.
you need to use static block to mock it.
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
#ExtendWith(MockitoExtension.class)
public class UsaTesteEstaticoTest {
#InjectMocks
UsaTesteEstatico usaTesteEstatico;
#BeforeAll
public static void init(){
Mockito.mockStatic(TesteEstatico.class);
}
#Test
void teste1(){
Mockito.when(TesteEstatico.teste())
.thenReturn("BANANA");
String res = usaTesteEstatico.metodoParaTeste1();
System.out.println(res);
}
#Test
void teste2(){
Mockito.when(TesteEstatico.teste())
.thenReturn("LARANJA");
String res = usaTesteEstatico.metodoParaTeste2();
System.out.println(res);
}
}

How to mock for custom Event?

MyEvent extends EventObject.
public class MyEvent extends EventObject {
private int buttonName;
public void setNum( int num) {
this.num= num;
}
public int getNum(){
return num;
}
public MyEvent(Object source) {
super(source);
}
}
With mockito-all-2.0.2-beta, I do mock the above Event for a unit test
import org.junit.After;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import org.junit.Before;
import org.junit.Test;
import static org.mockito.Mockito.mock;
public class MyEventTest {
public MyEventTest() {
}
#Before
public void setUp() {
}
#After
public void tearDown() {
}
/**
* Test of getButtonNum method, of class MyEvent.
*/
#Test
public void testGetButtonNumEqual() {
System.out.println("setButtonNum");
MyEvent evt = mock(MyEvent.class);
int buttonNum = 1;
evt.setButtonNum(buttonNum);
int result = evt.getButtonNum();
System.out.println(buttonNum);
System.out.println(result);
assertEquals(buttonNum, result);
}
/**
* Test of getButtonNum method, of class MyEvent.
*/
#Test
public void testGetButtonNumNotEqual() {
System.out.println("setButtonNum");
MyEvent evt = mock(MyEvent.class);
int buttonNum = 2;
int notEqualNum = 1;
evt.setButtonNum(buttonNum);
int result = evt.getButtonNum();
System.out.println(buttonNum);
System.out.println(result);
assertNotEquals(notEqualNum, result);
}
}
First Test is failed and Second Test is passed. The print output is below.
{
setButtonNum
1
0
and
setButtonNum
2
0
}
I would like to know why the first test is fail and how to do unit test a custom event.
Please, let me know what mistake I did. I appreciate your help. Thanks.

junit error: initiallization error: coused an error: no rannable method

I am having this error while all my methods in this test are successful - all of them return true. Also tried to make boolean for each method call and use only one call for assertTrue(a && b && c && d && e).
I run the junit test in java - netbeans.
My junit code (first time using junit) is:
package redis_fast_algo;
import data_sets.FastSortAlgoData;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
public class FastSortAlgoStudyTest {
private DataMaker dataMake;
private final static boolean BEFORE_DELETE = false;
public FastSortAlgoStudyTest() {
}
#BeforeClass
public static void setUpClass() {
}
#AfterClass
public static void tearDownClass() {
}
#Before
public void setUp() {
dataMake = new DataMaker();
}
#After
public void tearDown() {
}
/**
* Test of study method, of class FastSortAlgoStudy.
*/
#Test
public void testStudy() throws Exception {
System.out.println("study");
FastSortAlgoData data = dataMake.getData(5);
FastSortAlgoStudy instance = new FastSortAlgoStudy(data);
FastSortAlgoTest testStudy = new FastSortAlgoTest(data);
testStudy.prepareForTest();
instance.study();
assertTrue(testStudy.testCheckFolderStudyCounter(BEFORE_DELETE));
assertTrue(testStudy.testCheckBodyPart(BEFORE_DELETE));
assertTrue(testStudy.testCheckTitlePart(BEFORE_DELETE));
assertTrue(testStudy.testCheckFoldersHistoryWithData(BEFORE_DELETE));
assertTrue(testStudy.testRemoveFolderHistory());
}
}

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.

Is it possible to use partial mocking for private static methods in PowerMock?

From the examples on the PowerMock homepage, I see the following example for partially mocking a private method with Mockito:
#RunWith(PowerMockRunner.class)
// We prepare PartialMockClass for test because it's final or we need to mock private or static methods
#PrepareForTest(PartialMockClass.class)
public class YourTestCase {
#Test
public void privatePartialMockingWithPowerMock() {
PartialMockClass classUnderTest = PowerMockito.spy(new PartialMockClass());
// use PowerMockito to set up your expectation
PowerMockito.doReturn(value).when(classUnderTest, "methodToMock", "parameter1");
// execute your test
classUnderTest.execute();
// Use PowerMockito.verify() to verify result
PowerMockito.verifyPrivate(classUnderTest, times(2)).invoke("methodToMock", "parameter1");
}
However, this approach does not seem to work when the private method we wish to mock is static. I wish to create a partial mock of the below class, with the readFile method mocked:
package org.rich.powermockexample;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import static com.google.common.io.Files.readLines;
public class DataProvider {
public static List<String> getData() {
List<String> data = null;
try {
data = readFile();
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
private static List<String> readFile() throws IOException {
File file = new File("/some/path/to/file");
List<String> lines = readLines(file, Charset.forName("utf-8"));
return lines;
}
}
Please could someone let me know how this can be achieved?
After doing a bit more research, it seems that PowerMockito.spy() and PowerMockito.doReturn() are what is required here:
package com.richashworth.powermockexample;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
#RunWith(PowerMockRunner.class)
#PrepareForTest({DataProvider.class})
public class ResultsWriterTest {
private static List<String> mockData = new ArrayList<String>();
private ResultsWriter resultsWriter;
#BeforeClass
public static void setUpOnce() {
final String firstLine = "Line 1";
final String secondLine = "Line 2";
mockData.add(firstLine);
mockData.add(secondLine);
}
#Before
public void setUp() {
resultsWriter = new ResultsWriter();
}
#Test
public void testGetDataAsString() throws Exception {
PowerMockito.spy(DataProvider.class);
PowerMockito.doReturn(mockData).when(DataProvider.class, "readFile");
final String expectedData = "Line 1\nLine 2\n";
final String returnedString = resultsWriter.getDataAsString();
assertEquals(expectedData, returnedString);
}
}
For further details and the complete code listing, check out my blog post here: https://richashworth.com/post/turbocharge-your-mocking-framework-with-powermock/
Test class:
#RunWith(PowerMockRunner.class)
#PrepareForTest(DataProvider.class)
public class DataProviderTest {
#Test
public void testGetDataWithMockedRead() throws Exception {
mockStaticPartial(DataProvider.class, "readFile");
Method[] methods = MemberMatcher.methods(DataProvider.class, "readFile");
expectPrivate(DataProvider.class, methods[0]).andReturn(Arrays.asList("ohai", "kthxbye"));
replay(DataProvider.class);
List<String> theData = DataProvider.getData();
assertEquals("ohai", theData.get(0));
assertEquals("kthxbye", theData.get(1));
}
}
Class being tested (basically yours):
public class DataProvider {
public static List<String> getData() {
try {
return readFile();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static List<String> readFile() throws IOException {
File file = new File("/some/path/to/file");
return readLines(file, Charset.forName("utf-8"));
}
}
In general, only use static mocking for classes that are beyond your control (e.g. java.io.File). Since DataProvider and readFile are your own, refactor DataProvider into a proper class (i.e. make its methods non-static), pull out readFile into a helper object and then mock that. See this answer https://stackoverflow.com/a/8819339/116509.

Categories