Related
Is it possible to test code that is written in lambda function that is passed inside the method process?
#AllArgsConstructor
public class JsonController {
private final JsonElementProcessingService jsonElementProcessingService;
private final JsonObjectProcessingService jsonObjectProcessingService;
private final JsonArrayProcessingService jsonArrayProcessingService;
public void process(String rawJson) {
jsonElementProcessingService.process(json -> {
JsonElement element = new JsonParser().parse(json);
if (element.isJsonArray()) {
return jsonArrayProcessingService.process(element.getAsJsonArray());
} else {
return jsonObjectProcessingService.process(element.getAsJsonObject());
}
}, rawJson);
}
}
Since the lambda is lazy the function is not invoked (Function::apply) when I call JsonController::process so is there any way to check that jsonArrayProcessingService::process is called?
#RunWith(JMockit.class)
public class JsonControllerTest {
#Injectable
private JsonElementProcessingService jsonElementProcessingService;
#Injectable
private JsonObjectProcessingService jsonObjectProcessingService;
#Injectable
private JsonArrayProcessingService jsonArrayProcessingService;
#Tested
private JsonController jsonController;
#Test
public void test() {
jsonController.process("[{\"key\":1}]");
// how check here that jsonArrayProcessingService was invoked?
}
}
Just make it testable (and readable) by converting it to a method:
public void process(String rawJson) {
jsonElementProcessingService.process(this::parse, rawJson);
}
Object parse(String json) {
JsonElement element = new JsonParser().parse(json);
if (element.isJsonArray()) {
return jsonArrayProcessingService.process(element.getAsJsonArray());
} else {
return jsonObjectProcessingService.process(element.getAsJsonObject());
}
}
The relevant guiding principles I personally follow are:
anytime my lambdas require curly brackets, convert them to a method
organise code so that it can be unit tested
You may need to change the return type of the parse method to match whatever your processing services (which you didn’t show) return.
Given its relatively-basic redirection logic, don't you just want to confirm which of the #Injectables got called:
#Test
public void test() {
jsonController.process("[{\"key\":1}]");
new Verifications() {{
jsonArrayProcessingService.process(withInstanceOf(JsonArray.class));
}};
}
In a previous post Creating a ToolTip Managed bean
I was able to create a manged bean to collect and display tooltip text with only a single lookup and store them in an Application Scope variable. This has worked great.
I am on the rather steep part of the JAVA learning curve so please forgive me.
I have another managed bean requirement to create a HashMap Application Scope but this time it needs to be of a type String, Object. The application is where I have a single 'master' database that contains most of the code, custom controls, and XPages. This Master Database will point to One or More application databases that will store the Notes Documents specific to the application in question. So I have created in the Master a series of Application Documents that specify the RepIDs of the Application, Help and Rules databases specific to the Application along with a number of other pieces of information about the Application. This should allow me to reuse the same custom control that will open the specific DB by passing it the Application Name. As an example the Master Design DB might point to "Purchasing", "Customer Complaints", "Travel Requests" etc.
The code below works to load and store the HashMap, but I am having trouble retrieving the the data.
The compiler shows two errors one at the public Object get(String key) method and the other at mapValue = this.internalMap.get(key); in the getAppRepID method I think that it is mainly syntax but not sure. I have commented the error in the code where it appears.
/**
*This Class makes the variables that define an application within Workflo!Approval
*available as an ApplicationScope variable.
*/
package ca.wfsystems.wfsAppUtils;
import lotus.domino.Base;
import lotus.domino.Session;
import lotus.domino.Database;
import lotus.domino.View;
import lotus.domino.NotesException;
import lotus.domino.ViewColumn;
import lotus.domino.ViewEntry;
import lotus.domino.ViewEntryCollection;
import lotus.domino.Name;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import com.ibm.xsp.extlib.util.ExtLibUtil;
/**
* #author Bill Fox Workflo Systems WFSystems.ca
* July 2014
* This class is provided as part of the Workflo!Approval Product
* and can be reused within this application.
* If copied to a different application please retain this attribution.
*
*/
public abstract class ApplicationUtils implements Serializable, Map<String, Object> {
private static final long serialVersionUID = 1L;
private Session s;
private Name serverName;
private String repID;
private String thisKey;
private ViewEntryCollection formVECol;
private Vector formNames;
private Database thisDB;
private Database appDB;
private View appView;
private View formView;
private ViewEntry formVE;
private ViewEntry tFormVE;
private ViewEntry ve;
private ViewEntry tVE;
private ViewEntryCollection veCol;
private final Map<String, Object> internalMap = new HashMap<String, Object>();
public ApplicationUtils() {
this.populateMap(internalMap);
}
private void populateMap(Map<String, Object> theMap) {
try{
s = ExtLibUtil.getCurrentSession();
//serverName = s.createName(s.getServerName());
thisDB = s.getCurrentDatabase();
appView = thisDB.getView("vwWFSApplications");
veCol = appView.getAllEntries();
ve = veCol.getFirstEntry();
ViewEntry tVE = null;
while (ve != null) {
rtnValue mapValue = new rtnValue();
tVE = veCol.getNextEntry(ve);
Vector colVal = ve.getColumnValues();
thisKey = colVal.get(0).toString();
mapValue.setRepID(colVal.get(2).toString());
// ...... load the rest of the values .......
theMap.put(thisKey, mapValue);
recycleObjects(ve);
ve = tVE;
}
}catch(NotesException e){
System.out.println(e.toString());
}finally{
recycleObjects(ve, veCol, appView, tVE);
}
}
public class rtnValue{
private String RepID;
private String HelpRepID;
private String RuleRepID;
private Vector FormNames;
public String getRepID() {
return RepID;
}
public void setRepID(String repID) {
RepID = repID;
}
public String getHelpRepID() {
return HelpRepID;
}
public void setHelpRepID(String helpRepID) {
HelpRepID = helpRepID;
}
public String getRuleRepID() {
return RuleRepID;
}
public void setRuleRepID(String ruleRepID) {
RuleRepID = ruleRepID;
}
public Vector getFormNames() {
return FormNames;
}
public void setFormNames(Vector formNames) {
FormNames = formNames;
}
}
public void clear() {
this.internalMap.clear();
this.populateMap(this.internalMap);
}
public boolean containsKey(Object key) {
return this.internalMap.containsKey(key);
}
public boolean containsValue(Object value) {
return this.internalMap.containsValue(value);
}
public Set<java.util.Map.Entry<String, Object>> entrySet() {
return this.internalMap.entrySet();
}
public Object get(String key) {
//error on Object get Method must return a result of type Object
try {
if (this.internalMap.containsKey(key)) {
return this.internalMap.get(key);
}
} catch (Exception e) {
System.out.println(e.toString());
rtnValue newMap = new rtnValue();
return newMap;
}
}
public boolean isEmpty() {
return this.internalMap.isEmpty();
}
public Set<String> keySet() {
return this.internalMap.keySet();
}
public Object put(String key, Object value) {
return this.internalMap.put(key, value);
}
public Object remove(Object key) {
return this.internalMap.remove(key);
}
public int size() {
return this.internalMap.size();
}
public Collection<Object> values() {
return this.internalMap.values();
}
public void putAll(Map<? extends String, ? extends Object> m) {
this.internalMap.putAll(m);
}
public String getAppRepID(String key){
/*get the Replica Id of the application database
* not sure this is the correct way to call this
*/
rtnValue mapValue = new rtnValue();
mapValue = this.internalMap.get(key);
//error on line above Type Mismatch: can not convert Object to ApplicationUtils.rtnValue
String repID = mapValue.getRepID();
}
public static void recycleObjects(Object... args) {
for (Object o : args) {
if (o != null) {
if (o instanceof Base) {
try {
((Base) o).recycle();
} catch (Throwable t) {
// who cares?
}
}
}
}
}
}
For the get() method, the way I handle that kind of situation is create a variable of the correct data type as null, in my try/catch set the variable, and at the end return the variable. So:
Object retVal = null;
try....
return retVal;
For the other error, if you right-click on the error marker, it might give you the opportunity to cast the variable to rtnValue, so:
mapValue = (rtnValue) this.internalMap.get(key)
If you haven't got it, Head First Java was a useful book for getting my head around some Java concepts. It's also worth downloading the FindBugs plugin for Domino Designer from OpenNTF. It will identify errors as well as bad practices. Just ignore the errors in the "local" package!
The problem is that there is an execution path that do not return nothing
public Object get(String key) {
//error on Object get Method must return a result of type Object
try {
if (this.internalMap.containsKey(key)) { // false
return this.internalMap.get(key);
}
} catch (Exception e) {
System.out.println(e.toString());
rtnValue newMap = new rtnValue();
return newMap;
}
}
if key is not present in the internalMap, nothing is thrown, then that method do not return anything.
To fix the problem, return the newMap at the end.
public Object get(String key) {
//error on Object get Method must return a result of type Object
try {
if (this.internalMap.containsKey(key)) {
return this.internalMap.get(key);
}
} catch (Exception e) {
System.out.println(e.toString());
}
rtnValue newMap = new rtnValue();
return newMap;
}
You can inline the return to save the allocation (which is what the compiler will do anyway). I didn't do it just to make it clear in the example.
But still you have a compiler error in getAppRepID method. You are expecting a rtnValue but you send back an Object. You must cast there.
The appropriate way to handle this is, if you know that all values are of a given type, create the map with the proper type.
Have you tried making your internalMap a map of rtnValue instances (so )?
I have an ExecutorService that is used to handle a stream of tasks. The tasks are represented by my DaemonTask class, and each task builds a response object which is passed to a response call (outside the scope of this question). I am using a switch statement to spawn the appropriate task based on a task id int. It looks something like;
//in my api listening thread
executorService.submit(DaemonTask.buildTask(int taskID));
//daemon task class
public abstract class DaemonTask implements Runnable {
public static DaemonTask buildTask(int taskID) {
switch(taskID) {
case TASK_A_ID: return new WiggleTask();
case TASK_B_ID: return new WobbleTask();
// ...very long list ...
case TASK_ZZZ_ID: return new WaggleTask();
}
}
public void run() {
respond(execute());
}
public abstract Response execute();
}
All of my task classes (such as WiggleTask() ) extend DaemonTask and provide an implementation for the execute() method.
My question is simply; is this pattern reasonable? Something feels wrong when I look at my huge switch case with all its return statements. I have tried to come up with a more elegant lookup table solution using reflection in some way but can't seem to figure out an approach that would work.
Do you really need so many classes? You could have one method per taskId.
final ResponseHandler handler = ... // has many methods.
// use a map or array or enum to translate transIds into method names.
final Method method = handler.getClass().getMethod(taskArray[taskID]);
executorService.submit(new Callable<Void>() {
public Void call() throws Exception {
method.invoke(handler);
}
});
If you have to have many classes, you can do
// use a map or array or enum to translate transIds into methods.
final Runnable runs = Class.forName(taskClassArray[taskID]).newInstance();
executorService.submit(new Callable<Void>() {
public Void call() throws Exception {
runs.run();
}
});
You can use an enum:
public enum TaskBuilder
{
// Task definitions
TASK_A_ID(1){
#Override
public DaemonTask newTask()
{
return new WiggleTask();
}
},
// etc
// Build lookup map
private static final Map<Integer, TaskBuilder> LOOKUP_MAP
= new HashMap<Integer, TaskBuilder>();
static {
for (final TaskBuilder builder: values())
LOOKUP_MAP.put(builder.taskID, builder);
}
private final int taskID;
public abstract DaemonTask newTask();
TaskBuilder(final int taskID)
{
this.taskID = taskID;
}
// Note: null needs to be handled somewhat
public static TaskBuilder fromTaskID(final int taskID)
{
return LOOKUP_MAP.get(taskID);
}
}
With such an enum, you can then do:
TaskBuilder.fromTaskID(taskID).newTask();
Another possibility is to use a constructor field instead of a method, that is, you use reflection. It is much easier to write and it works OK, but exception handling then becomes nothing short of a nightmare:
private enum TaskBuilder
{
TASK_ID_A(1, WiggleTask.class),
// others
// Build lookup map
private static final Map<Integer, TaskBuilder> LOOKUP_MAP
= new HashMap<Integer, TaskBuilder>();
static {
for (final TaskBuilder builder: values())
LOOKUP_MAP.put(builder.taskID, builder);
}
private final int index;
private final Constructor<? extends DaemonTask> constructor;
TaskBuilder(final int index, final Class<? extends DaemonTask> c)
{
this.index = index;
// This can fail...
try {
constructor = c.getConstructor();
} catch (NoSuchMethodException e) {
throw new ExceptionInInitializerError(e);
}
}
// Ewww, three exceptions :(
public DaemonTask newTask()
throws IllegalAccessException, InvocationTargetException,
InstantiationException
{
return constructor.newInstance();
}
// Note: null needs to be handled somewhat
public static TaskBuilder fromTaskID(final int taskID)
{
return LOOKUP_MAP.get(taskID);
}
}
This enum can be used the same way as the other one.
Is there a way to set my own custom test case names when using parameterized tests in JUnit4?
I'd like to change the default — [Test class].runTest[n] — to something meaningful.
This feature has made it into JUnit 4.11.
To use change the name of parameterized tests, you say:
#Parameters(name="namestring")
namestring is a string, which can have the following special placeholders:
{index} - the index of this set of arguments. The default namestring is {index}.
{0} - the first parameter value from this invocation of the test.
{1} - the second parameter value
and so on
The final name of the test will be the name of the test method, followed by the namestring in brackets, as shown below.
For example (adapted from the unit test for the Parameterized annotation):
#RunWith(Parameterized.class)
static public class FibonacciTest {
#Parameters( name = "{index}: fib({0})={1}" )
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
{ 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
}
private final int fInput;
private final int fExpected;
public FibonacciTest(int input, int expected) {
fInput= input;
fExpected= expected;
}
#Test
public void testFib() {
assertEquals(fExpected, fib(fInput));
}
private int fib(int x) {
// TODO: actually calculate Fibonacci numbers
return 0;
}
}
will give names like testFib[1: fib(1)=1] and testFib[4: fib(4)=3]. (The testFib part of the name is the method name of the #Test).
Looking at JUnit 4.5, its runner clearly doesn't support that, as that logic is buried inside a private class inside the Parameterized class. You could not use the JUnit Parameterized runner, and create your own instead which would understand the concept of names (which leads to the question of how you might set a name ...).
From a JUnit perspective, it would be nice if instead of (or in addition to) just passing an increment, they would pass the comma delimited arguments. TestNG does this. If the feature is important to you, you can comment on the yahoo mailing list referenced at www.junit.org.
I recently came across the same problem when using JUnit 4.3.1. I implemented a new class which extends Parameterized called LabelledParameterized. It has been tested using JUnit 4.3.1, 4.4 and 4.5. It reconstructs the Description instance using the String representation of the first argument of each parameter array from the #Parameters method. You can see the code for this at:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../LabelledParameterized.java?r=3789
and an example of its use at:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../ServerBuilderTest.java?r=3789
The test description formats nicely in Eclipse which is what I wanted since this makes failed tests a lot easier to find! I will probably further refine and document the classes over the next few days/weeks. Drop the '?' part of the URLs if you want the bleeding edge. :-)
To use it, all you have to do is copy that class (GPL v3), and change #RunWith(Parameterized.class) to #RunWith(LabelledParameterized.class) assuming the first element of your parameter list is a sensible label.
I don't know if any later releases of JUnit address this issue but even if they did, I can't update JUnit since all my co-developers would have to update too and we have higher priorities than re-tooling. Hence the work in the class to be compilable by multiple versions of JUnit.
Note: there is some reflection jiggery-pokery so that it runs across the different JUnit versions as listed above. The version specifically for JUnit 4.3.1 can be found here and, for JUnit 4.4 and 4.5, here.
With Parameterized as a model, I wrote my own custom test runner / suite -- only took about half an hour. It's slightly different from darrenp's LabelledParameterized in that it lets you specify a name explicitly rather than relying on the first parameter's toString().
It also doesn't use arrays because I hate arrays. :)
public class PolySuite extends Suite {
// //////////////////////////////
// Public helper interfaces
/**
* Annotation for a method which returns a {#link Configuration}
* to be injected into the test class constructor
*/
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public static #interface Config {
}
public static interface Configuration {
int size();
Object getTestValue(int index);
String getTestName(int index);
}
// //////////////////////////////
// Fields
private final List<Runner> runners;
// //////////////////////////////
// Constructor
/**
* Only called reflectively. Do not use programmatically.
* #param c the test class
* #throws Throwable if something bad happens
*/
public PolySuite(Class<?> c) throws Throwable {
super(c, Collections.<Runner>emptyList());
TestClass testClass = getTestClass();
Class<?> jTestClass = testClass.getJavaClass();
Configuration configuration = getConfiguration(testClass);
List<Runner> runners = new ArrayList<Runner>();
for (int i = 0, size = configuration.size(); i < size; i++) {
SingleRunner runner = new SingleRunner(jTestClass, configuration.getTestValue(i), configuration.getTestName(i));
runners.add(runner);
}
this.runners = runners;
}
// //////////////////////////////
// Overrides
#Override
protected List<Runner> getChildren() {
return runners;
}
// //////////////////////////////
// Private
private Configuration getConfiguration(TestClass testClass) throws Throwable {
return (Configuration) getConfigMethod(testClass).invokeExplosively(null);
}
private FrameworkMethod getConfigMethod(TestClass testClass) {
List<FrameworkMethod> methods = testClass.getAnnotatedMethods(Config.class);
if (methods.isEmpty()) {
throw new IllegalStateException("#" + Config.class.getSimpleName() + " method not found");
}
if (methods.size() > 1) {
throw new IllegalStateException("Too many #" + Config.class.getSimpleName() + " methods");
}
FrameworkMethod method = methods.get(0);
int modifiers = method.getMethod().getModifiers();
if (!(Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new IllegalStateException("#" + Config.class.getSimpleName() + " method \"" + method.getName() + "\" must be public static");
}
return method;
}
// //////////////////////////////
// Helper classes
private static class SingleRunner extends BlockJUnit4ClassRunner {
private final Object testVal;
private final String testName;
SingleRunner(Class<?> testClass, Object testVal, String testName) throws InitializationError {
super(testClass);
this.testVal = testVal;
this.testName = testName;
}
#Override
protected Object createTest() throws Exception {
return getTestClass().getOnlyConstructor().newInstance(testVal);
}
#Override
protected String getName() {
return testName;
}
#Override
protected String testName(FrameworkMethod method) {
return testName + ": " + method.getName();
}
#Override
protected void validateConstructor(List<Throwable> errors) {
validateOnlyOneConstructor(errors);
}
#Override
protected Statement classBlock(RunNotifier notifier) {
return childrenInvoker(notifier);
}
}
}
And an example:
#RunWith(PolySuite.class)
public class PolySuiteExample {
// //////////////////////////////
// Fixture
#Config
public static Configuration getConfig() {
return new Configuration() {
#Override
public int size() {
return 10;
}
#Override
public Integer getTestValue(int index) {
return index * 2;
}
#Override
public String getTestName(int index) {
return "test" + index;
}
};
}
// //////////////////////////////
// Fields
private final int testVal;
// //////////////////////////////
// Constructor
public PolySuiteExample(int testVal) {
this.testVal = testVal;
}
// //////////////////////////////
// Test
#Ignore
#Test
public void odd() {
assertFalse(testVal % 2 == 0);
}
#Test
public void even() {
assertTrue(testVal % 2 == 0);
}
}
You may also want to try JUnitParams: https://github.com/Pragmatists/JUnitParams
from junit4.8.2, you can create your own MyParameterized class by simply copy Parameterized class. change the getName() and testName() methods in TestClassRunnerForParameters.
None of it was working for me, so I got the source for Parameterized and modified it create a a new test runner. I didn't have to change much but IT WORKS!!!
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.junit.Assert;
import org.junit.internal.runners.ClassRoadie;
import org.junit.internal.runners.CompositeRunner;
import org.junit.internal.runners.InitializationError;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.internal.runners.MethodValidator;
import org.junit.internal.runners.TestClass;
import org.junit.runner.notification.RunNotifier;
public class LabelledParameterized extends CompositeRunner {
static class TestClassRunnerForParameters extends JUnit4ClassRunner {
private final Object[] fParameters;
private final String fParameterFirstValue;
private final Constructor<?> fConstructor;
TestClassRunnerForParameters(TestClass testClass, Object[] parameters, int i) throws InitializationError {
super(testClass.getJavaClass()); // todo
fParameters = parameters;
if (parameters != null) {
fParameterFirstValue = Arrays.asList(parameters).toString();
} else {
fParameterFirstValue = String.valueOf(i);
}
fConstructor = getOnlyConstructor();
}
#Override
protected Object createTest() throws Exception {
return fConstructor.newInstance(fParameters);
}
#Override
protected String getName() {
return String.format("%s", fParameterFirstValue);
}
#Override
protected String testName(final Method method) {
return String.format("%s%s", method.getName(), fParameterFirstValue);
}
private Constructor<?> getOnlyConstructor() {
Constructor<?>[] constructors = getTestClass().getJavaClass().getConstructors();
Assert.assertEquals(1, constructors.length);
return constructors[0];
}
#Override
protected void validate() throws InitializationError {
// do nothing: validated before.
}
#Override
public void run(RunNotifier notifier) {
runMethods(notifier);
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public static #interface Parameters {
}
private final TestClass fTestClass;
public LabelledParameterized(Class<?> klass) throws Exception {
super(klass.getName());
fTestClass = new TestClass(klass);
MethodValidator methodValidator = new MethodValidator(fTestClass);
methodValidator.validateStaticMethods();
methodValidator.validateInstanceMethods();
methodValidator.assertValid();
int i = 0;
for (final Object each : getParametersList()) {
if (each instanceof Object[])
add(new TestClassRunnerForParameters(fTestClass, (Object[]) each, i++));
else
throw new Exception(String.format("%s.%s() must return a Collection of arrays.", fTestClass.getName(), getParametersMethod().getName()));
}
}
#Override
public void run(final RunNotifier notifier) {
new ClassRoadie(notifier, fTestClass, getDescription(), new Runnable() {
public void run() {
runChildren(notifier);
}
}).runProtected();
}
private Collection<?> getParametersList() throws IllegalAccessException, InvocationTargetException, Exception {
return (Collection<?>) getParametersMethod().invoke(null);
}
private Method getParametersMethod() throws Exception {
List<Method> methods = fTestClass.getAnnotatedMethods(Parameters.class);
for (Method each : methods) {
int modifiers = each.getModifiers();
if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))
return each;
}
throw new Exception("No public static parameters method on class " + getName());
}
public static Collection<Object[]> eachOne(Object... params) {
List<Object[]> results = new ArrayList<Object[]>();
for (Object param : params)
results.add(new Object[] { param });
return results;
}
}
You can create a method like
#Test
public void name() {
Assert.assertEquals("", inboundFileName);
}
While I wouldn't use it all the time it would be useful to figure out exactly which test number 143 is.
I make extensive use of static import for Assert and friends, so it is easy for me to redefine assertion:
private <T> void assertThat(final T actual, final Matcher<T> expected) {
Assert.assertThat(editThisToDisplaySomethingForYourDatum, actual, expected);
}
For example, you could add a "name" field to your test class, initialized in the constructor, and display that on test failure. Just pass it in as the first elements of your parameters array for each test. This also helps label the data:
public ExampleTest(final String testLabel, final int one, final int two) {
this.testLabel = testLabel;
// ...
}
#Parameters
public static Collection<Object[]> data() {
return asList(new Object[][]{
{"first test", 3, 4},
{"second test", 5, 6}
});
}
A workaround would be to catch and nest all Throwables into a new Throwable with a custom message that contains all information about the parameters. The message would appear in the stack trace.
This works whenever a test fails for all assertions, errors and exceptions as they are all subclasses of Throwable.
My code looks like this:
#RunWith(Parameterized.class)
public class ParameterizedTest {
int parameter;
public ParameterizedTest(int parameter) {
super();
this.parameter = parameter;
}
#Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] { {1}, {2} });
}
#Test
public void test() throws Throwable {
try {
assertTrue(parameter%2==0);
}
catch(Throwable thrown) {
throw new Throwable("parameter="+parameter, thrown);
}
}
}
The stack trace of the failed test is:
java.lang.Throwable: parameter=1
at sample.ParameterizedTest.test(ParameterizedTest.java:34)
Caused by: java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:92)
at org.junit.Assert.assertTrue(Assert.java:43)
at org.junit.Assert.assertTrue(Assert.java:54)
at sample.ParameterizedTest.test(ParameterizedTest.java:31)
... 31 more
When you want the parameter values in test name then you can do something like -
#ParameterizedTest(name="{index} {arguments} then return false" )
#ValueSource(strings = {"false","FALSE"," ","123","abc"})
#DisplayName("When Feature JVM argument is ")
void test_Feature_JVM_Argument_Is_Empty_Or_Blank_Strings_Or_False(String params) {
System.setProperty("FeatureName", params);
assertFalse(Boolean.parseBoolean(System.getProperty("FeatureName")));
}
Test name will look like -
JUnit Test image
Check out JUnitParams as dsaff mentioned, works using ant to build parameterized test method descriptions in the html report.
This was after trying LabelledParameterized and finding that it although it works with eclipse it does not work with ant as far as the html report is concerned.
Cheers,
Since the parameter accessed (e.g. with "{0}" always returns the toString() representation, one workaround would be to make an anonymous implementation and override toString() in each case. For example:
public static Iterable<? extends Object> data() {
return Arrays.asList(
new MyObject(myParams...) {public String toString(){return "my custom test name";}},
new MyObject(myParams...) {public String toString(){return "my other custom test name";}},
//etc...
);
}
Parameterized test is calling toString() internally.
If you create an object wrapper overiding toString(), it will change the names of the test.
Here is an example, I answered in other post.
https://stackoverflow.com/a/67023556/1839360
For a more complex object you may do the following (example with JUnit 4):
#RunWith(Parameterized.class)
public class MainTest {
private static Object[] makeSample(String[] array, int expectedLength) {
return new Object[]{array, expectedLength, Arrays.toString(array)};
}
#Parameterized.Parameters(name = "for input {2} length should equal {1}")
public static Collection<Object[]> data() {
return Arrays.asList(
makeSample(new String[]{"a"}, 1),
makeSample(new String[]{"a", "b"}, 2)
);
}
private final int expectedLength;
private final String[] array;
public MainTest(String[] array, int expectedLength, String strArray) {
this.array = array;
this.expectedLength = expectedLength;
}
#Test
public void should_have_expected_length() {
assertEquals(expectedLength, array.length);
}
}
The trick here is to use one input parameter as a string describing either some part of input or the whole test case.
Before adding third parameter it looked like this
And after like this
Is there a way to set my own custom test case names when using parameterized tests in JUnit4?
I'd like to change the default — [Test class].runTest[n] — to something meaningful.
This feature has made it into JUnit 4.11.
To use change the name of parameterized tests, you say:
#Parameters(name="namestring")
namestring is a string, which can have the following special placeholders:
{index} - the index of this set of arguments. The default namestring is {index}.
{0} - the first parameter value from this invocation of the test.
{1} - the second parameter value
and so on
The final name of the test will be the name of the test method, followed by the namestring in brackets, as shown below.
For example (adapted from the unit test for the Parameterized annotation):
#RunWith(Parameterized.class)
static public class FibonacciTest {
#Parameters( name = "{index}: fib({0})={1}" )
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
{ 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
}
private final int fInput;
private final int fExpected;
public FibonacciTest(int input, int expected) {
fInput= input;
fExpected= expected;
}
#Test
public void testFib() {
assertEquals(fExpected, fib(fInput));
}
private int fib(int x) {
// TODO: actually calculate Fibonacci numbers
return 0;
}
}
will give names like testFib[1: fib(1)=1] and testFib[4: fib(4)=3]. (The testFib part of the name is the method name of the #Test).
Looking at JUnit 4.5, its runner clearly doesn't support that, as that logic is buried inside a private class inside the Parameterized class. You could not use the JUnit Parameterized runner, and create your own instead which would understand the concept of names (which leads to the question of how you might set a name ...).
From a JUnit perspective, it would be nice if instead of (or in addition to) just passing an increment, they would pass the comma delimited arguments. TestNG does this. If the feature is important to you, you can comment on the yahoo mailing list referenced at www.junit.org.
I recently came across the same problem when using JUnit 4.3.1. I implemented a new class which extends Parameterized called LabelledParameterized. It has been tested using JUnit 4.3.1, 4.4 and 4.5. It reconstructs the Description instance using the String representation of the first argument of each parameter array from the #Parameters method. You can see the code for this at:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../LabelledParameterized.java?r=3789
and an example of its use at:
http://code.google.com/p/migen/source/browse/trunk/java/src/.../ServerBuilderTest.java?r=3789
The test description formats nicely in Eclipse which is what I wanted since this makes failed tests a lot easier to find! I will probably further refine and document the classes over the next few days/weeks. Drop the '?' part of the URLs if you want the bleeding edge. :-)
To use it, all you have to do is copy that class (GPL v3), and change #RunWith(Parameterized.class) to #RunWith(LabelledParameterized.class) assuming the first element of your parameter list is a sensible label.
I don't know if any later releases of JUnit address this issue but even if they did, I can't update JUnit since all my co-developers would have to update too and we have higher priorities than re-tooling. Hence the work in the class to be compilable by multiple versions of JUnit.
Note: there is some reflection jiggery-pokery so that it runs across the different JUnit versions as listed above. The version specifically for JUnit 4.3.1 can be found here and, for JUnit 4.4 and 4.5, here.
With Parameterized as a model, I wrote my own custom test runner / suite -- only took about half an hour. It's slightly different from darrenp's LabelledParameterized in that it lets you specify a name explicitly rather than relying on the first parameter's toString().
It also doesn't use arrays because I hate arrays. :)
public class PolySuite extends Suite {
// //////////////////////////////
// Public helper interfaces
/**
* Annotation for a method which returns a {#link Configuration}
* to be injected into the test class constructor
*/
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public static #interface Config {
}
public static interface Configuration {
int size();
Object getTestValue(int index);
String getTestName(int index);
}
// //////////////////////////////
// Fields
private final List<Runner> runners;
// //////////////////////////////
// Constructor
/**
* Only called reflectively. Do not use programmatically.
* #param c the test class
* #throws Throwable if something bad happens
*/
public PolySuite(Class<?> c) throws Throwable {
super(c, Collections.<Runner>emptyList());
TestClass testClass = getTestClass();
Class<?> jTestClass = testClass.getJavaClass();
Configuration configuration = getConfiguration(testClass);
List<Runner> runners = new ArrayList<Runner>();
for (int i = 0, size = configuration.size(); i < size; i++) {
SingleRunner runner = new SingleRunner(jTestClass, configuration.getTestValue(i), configuration.getTestName(i));
runners.add(runner);
}
this.runners = runners;
}
// //////////////////////////////
// Overrides
#Override
protected List<Runner> getChildren() {
return runners;
}
// //////////////////////////////
// Private
private Configuration getConfiguration(TestClass testClass) throws Throwable {
return (Configuration) getConfigMethod(testClass).invokeExplosively(null);
}
private FrameworkMethod getConfigMethod(TestClass testClass) {
List<FrameworkMethod> methods = testClass.getAnnotatedMethods(Config.class);
if (methods.isEmpty()) {
throw new IllegalStateException("#" + Config.class.getSimpleName() + " method not found");
}
if (methods.size() > 1) {
throw new IllegalStateException("Too many #" + Config.class.getSimpleName() + " methods");
}
FrameworkMethod method = methods.get(0);
int modifiers = method.getMethod().getModifiers();
if (!(Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new IllegalStateException("#" + Config.class.getSimpleName() + " method \"" + method.getName() + "\" must be public static");
}
return method;
}
// //////////////////////////////
// Helper classes
private static class SingleRunner extends BlockJUnit4ClassRunner {
private final Object testVal;
private final String testName;
SingleRunner(Class<?> testClass, Object testVal, String testName) throws InitializationError {
super(testClass);
this.testVal = testVal;
this.testName = testName;
}
#Override
protected Object createTest() throws Exception {
return getTestClass().getOnlyConstructor().newInstance(testVal);
}
#Override
protected String getName() {
return testName;
}
#Override
protected String testName(FrameworkMethod method) {
return testName + ": " + method.getName();
}
#Override
protected void validateConstructor(List<Throwable> errors) {
validateOnlyOneConstructor(errors);
}
#Override
protected Statement classBlock(RunNotifier notifier) {
return childrenInvoker(notifier);
}
}
}
And an example:
#RunWith(PolySuite.class)
public class PolySuiteExample {
// //////////////////////////////
// Fixture
#Config
public static Configuration getConfig() {
return new Configuration() {
#Override
public int size() {
return 10;
}
#Override
public Integer getTestValue(int index) {
return index * 2;
}
#Override
public String getTestName(int index) {
return "test" + index;
}
};
}
// //////////////////////////////
// Fields
private final int testVal;
// //////////////////////////////
// Constructor
public PolySuiteExample(int testVal) {
this.testVal = testVal;
}
// //////////////////////////////
// Test
#Ignore
#Test
public void odd() {
assertFalse(testVal % 2 == 0);
}
#Test
public void even() {
assertTrue(testVal % 2 == 0);
}
}
You may also want to try JUnitParams: https://github.com/Pragmatists/JUnitParams
from junit4.8.2, you can create your own MyParameterized class by simply copy Parameterized class. change the getName() and testName() methods in TestClassRunnerForParameters.
None of it was working for me, so I got the source for Parameterized and modified it create a a new test runner. I didn't have to change much but IT WORKS!!!
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.junit.Assert;
import org.junit.internal.runners.ClassRoadie;
import org.junit.internal.runners.CompositeRunner;
import org.junit.internal.runners.InitializationError;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.internal.runners.MethodValidator;
import org.junit.internal.runners.TestClass;
import org.junit.runner.notification.RunNotifier;
public class LabelledParameterized extends CompositeRunner {
static class TestClassRunnerForParameters extends JUnit4ClassRunner {
private final Object[] fParameters;
private final String fParameterFirstValue;
private final Constructor<?> fConstructor;
TestClassRunnerForParameters(TestClass testClass, Object[] parameters, int i) throws InitializationError {
super(testClass.getJavaClass()); // todo
fParameters = parameters;
if (parameters != null) {
fParameterFirstValue = Arrays.asList(parameters).toString();
} else {
fParameterFirstValue = String.valueOf(i);
}
fConstructor = getOnlyConstructor();
}
#Override
protected Object createTest() throws Exception {
return fConstructor.newInstance(fParameters);
}
#Override
protected String getName() {
return String.format("%s", fParameterFirstValue);
}
#Override
protected String testName(final Method method) {
return String.format("%s%s", method.getName(), fParameterFirstValue);
}
private Constructor<?> getOnlyConstructor() {
Constructor<?>[] constructors = getTestClass().getJavaClass().getConstructors();
Assert.assertEquals(1, constructors.length);
return constructors[0];
}
#Override
protected void validate() throws InitializationError {
// do nothing: validated before.
}
#Override
public void run(RunNotifier notifier) {
runMethods(notifier);
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public static #interface Parameters {
}
private final TestClass fTestClass;
public LabelledParameterized(Class<?> klass) throws Exception {
super(klass.getName());
fTestClass = new TestClass(klass);
MethodValidator methodValidator = new MethodValidator(fTestClass);
methodValidator.validateStaticMethods();
methodValidator.validateInstanceMethods();
methodValidator.assertValid();
int i = 0;
for (final Object each : getParametersList()) {
if (each instanceof Object[])
add(new TestClassRunnerForParameters(fTestClass, (Object[]) each, i++));
else
throw new Exception(String.format("%s.%s() must return a Collection of arrays.", fTestClass.getName(), getParametersMethod().getName()));
}
}
#Override
public void run(final RunNotifier notifier) {
new ClassRoadie(notifier, fTestClass, getDescription(), new Runnable() {
public void run() {
runChildren(notifier);
}
}).runProtected();
}
private Collection<?> getParametersList() throws IllegalAccessException, InvocationTargetException, Exception {
return (Collection<?>) getParametersMethod().invoke(null);
}
private Method getParametersMethod() throws Exception {
List<Method> methods = fTestClass.getAnnotatedMethods(Parameters.class);
for (Method each : methods) {
int modifiers = each.getModifiers();
if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))
return each;
}
throw new Exception("No public static parameters method on class " + getName());
}
public static Collection<Object[]> eachOne(Object... params) {
List<Object[]> results = new ArrayList<Object[]>();
for (Object param : params)
results.add(new Object[] { param });
return results;
}
}
You can create a method like
#Test
public void name() {
Assert.assertEquals("", inboundFileName);
}
While I wouldn't use it all the time it would be useful to figure out exactly which test number 143 is.
I make extensive use of static import for Assert and friends, so it is easy for me to redefine assertion:
private <T> void assertThat(final T actual, final Matcher<T> expected) {
Assert.assertThat(editThisToDisplaySomethingForYourDatum, actual, expected);
}
For example, you could add a "name" field to your test class, initialized in the constructor, and display that on test failure. Just pass it in as the first elements of your parameters array for each test. This also helps label the data:
public ExampleTest(final String testLabel, final int one, final int two) {
this.testLabel = testLabel;
// ...
}
#Parameters
public static Collection<Object[]> data() {
return asList(new Object[][]{
{"first test", 3, 4},
{"second test", 5, 6}
});
}
A workaround would be to catch and nest all Throwables into a new Throwable with a custom message that contains all information about the parameters. The message would appear in the stack trace.
This works whenever a test fails for all assertions, errors and exceptions as they are all subclasses of Throwable.
My code looks like this:
#RunWith(Parameterized.class)
public class ParameterizedTest {
int parameter;
public ParameterizedTest(int parameter) {
super();
this.parameter = parameter;
}
#Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] { {1}, {2} });
}
#Test
public void test() throws Throwable {
try {
assertTrue(parameter%2==0);
}
catch(Throwable thrown) {
throw new Throwable("parameter="+parameter, thrown);
}
}
}
The stack trace of the failed test is:
java.lang.Throwable: parameter=1
at sample.ParameterizedTest.test(ParameterizedTest.java:34)
Caused by: java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:92)
at org.junit.Assert.assertTrue(Assert.java:43)
at org.junit.Assert.assertTrue(Assert.java:54)
at sample.ParameterizedTest.test(ParameterizedTest.java:31)
... 31 more
When you want the parameter values in test name then you can do something like -
#ParameterizedTest(name="{index} {arguments} then return false" )
#ValueSource(strings = {"false","FALSE"," ","123","abc"})
#DisplayName("When Feature JVM argument is ")
void test_Feature_JVM_Argument_Is_Empty_Or_Blank_Strings_Or_False(String params) {
System.setProperty("FeatureName", params);
assertFalse(Boolean.parseBoolean(System.getProperty("FeatureName")));
}
Test name will look like -
JUnit Test image
Check out JUnitParams as dsaff mentioned, works using ant to build parameterized test method descriptions in the html report.
This was after trying LabelledParameterized and finding that it although it works with eclipse it does not work with ant as far as the html report is concerned.
Cheers,
Since the parameter accessed (e.g. with "{0}" always returns the toString() representation, one workaround would be to make an anonymous implementation and override toString() in each case. For example:
public static Iterable<? extends Object> data() {
return Arrays.asList(
new MyObject(myParams...) {public String toString(){return "my custom test name";}},
new MyObject(myParams...) {public String toString(){return "my other custom test name";}},
//etc...
);
}
Parameterized test is calling toString() internally.
If you create an object wrapper overiding toString(), it will change the names of the test.
Here is an example, I answered in other post.
https://stackoverflow.com/a/67023556/1839360
For a more complex object you may do the following (example with JUnit 4):
#RunWith(Parameterized.class)
public class MainTest {
private static Object[] makeSample(String[] array, int expectedLength) {
return new Object[]{array, expectedLength, Arrays.toString(array)};
}
#Parameterized.Parameters(name = "for input {2} length should equal {1}")
public static Collection<Object[]> data() {
return Arrays.asList(
makeSample(new String[]{"a"}, 1),
makeSample(new String[]{"a", "b"}, 2)
);
}
private final int expectedLength;
private final String[] array;
public MainTest(String[] array, int expectedLength, String strArray) {
this.array = array;
this.expectedLength = expectedLength;
}
#Test
public void should_have_expected_length() {
assertEquals(expectedLength, array.length);
}
}
The trick here is to use one input parameter as a string describing either some part of input or the whole test case.
Before adding third parameter it looked like this
And after like this