Java. Array type expected in lambda - java

So, I've got an object constructor:
public Func(Function<Var[], Var<T>> function, Var... arguments) {
// Function is a standart 1.8 class
//...
//secret stuff
}
I call it like that:
return new Func(new Function<Var[], Var>() {
#Override
public Var apply(Var[] args) {
return instance.getAttribute(args[0].value());
}
}, arguments[0].unpack(instance) // unpack(...) returns Var object
);
And it works. Now, my IDE (Intellij IDEA) suggests me to replace Function declaration with lambda. Okay, lets do it:
return new Func(
args -> instance.getAttribute(args[0].value()),
arguments[0].unpack(instance)
);
Now I have an error on args:
Array type expected; found: 'java.lang.Object'
So, apparently args now is Object. Why? Is that a bug in IDE or what?
Entire code:
Template:
public class Template {
public static void main(String[] args) {
SomeClass someClass = new SomeClass();
System.out.println(someMethod(someClass).value());
}
private static class SomeClass {
Var[] var = new Var[12];
SomeClass() {
var = new Var[12];
for ( int i = 0; i < var.length; i++) {
var[i] = new Var<>(i * 4);
}
}
Var getAttribute(int index) {
return var[index];
}
}
public static Var someMethod(SomeClass instance) {
return new Func(new Function<Var[], Var>() {
#Override
public Var apply(Var[] args) {
return instance.getAttribute((int)args[0].value());
}
}, new Var(4));
}
}
Var.java:
public class Var<T> {
private T value;
public Var(T value) {
this.value = value;
}
public T value() {
return value;
}
}
Func.java:
public class Func<T> extends Var<T> {
private Function<Var[], Var<T>> function;
private Var[] args;
public Func(Function<Var[], Var<T>> function, Var... args) {
super(null);
this.function = function;
this.args = args;
}
#Override
public T value() {
return function.apply(args).value();
}
}

The error message appers also in the Eclipse-IDE:
The type of the expression must be an array type but it resolved to Object
I think it is not an IDE-bug, neither in IntelliJ nor in Eclipse. The Compiler needs for the processing of a lambda expression always a target type which is a functional interface.
In the case of
args -> instance.getAttribute((int)args[0].value())
the target type is determined by the first argument of the Func-constructor
Function<Var[], Var<T>> function
However, this functional interface is a generic interface. Java compiles generics using type erasure which means the replacement of the generic parameter-types by the Object-type. Thus, the interface is compiled like
interface Function {
public Object apply(Object args);
}
and this is applied as target type. Thus, for args an Object-type instead of a Var[]-type is expected which results in an error message.
In case of an anonymous class this is different since more informations are provided for the determination of the target type.
new Function<Var[], Var>(){...}
explicitly contains the type-information. Because of this args is expected of Var[]-type and no error message is shown.
There are two possibilities to fix the error:
1) In the getAttribut-method cast args explicitly to Var[], i.e. replace
(int)args[0].value()
with
(int)((Var[])args)[0].value()
or 2) Don't use a generic interface i.e. change the interface to
interface Function {
public Var apply(Var[] args);
}
Then type information is preserved. Of course the rest of the code has to be adapted accordingly.

Related

Javaparser: get generic class of a method return type like List<MyClass>

I'm processing a Java source file using the java-parser library.
I have this situation in the analyzed source code:
List<Allergen> getList() {
return EnumSet.allOf(Allergen.class).stream()
.filter(this::isAllergicTo)
.collect(Collectors.toList());
}
I need to access the generic class of the List return type ( Allergen in this example)
I'm using an extension of VoidVisitorAdapter class in my analyzer
Here an extract of the code:
public class PlaceholderNormalizer extends VoidVisitorAdapter<String> {
...
...
#Override
public void visit(MethodDeclaration n, String arg) {
logger.debug("MethodDeclaration: {}", n.getName().asString());
logger.debug(" --- " + n.getTypeAsString()); // prints: List<Allergen>
// DO SOME STUFF
super.visit(n, arg);
}
...
...
}
I can obtain a Type object of the node MethodDeclaration using n.getType()
But, I don't understand how to retrieve the Allergen class.
Please I need some help
In case of this method the n.getType will return instance of ClassOrInterfaceType. To get to generic parameter you can obtain it as this type and use ClassOrInterfaceType::getTypeArguments method :
#Override
public void visit(MethodDeclaration n, String arg) {
Type type = n.getType();
if(type instanceof ClassOrInterfaceType) {
ClassOrInterfaceType classOrInterfaceType = type.asClassOrInterfaceType();
Optional<NodeList<Type>> typeArguments = classOrInterfaceType.getTypeArguments();
//list of arguments might have different length
typeArguments.ifPresent(types -> System.out.println(types.get(0)));
}
// DO SOME STUFF
super.visit(n, arg);
}
This will print Allergen.

Generic Singleton Factory

While reading online, I came across the following:
public interface UnaryFunction<T>
{
T apply(T arg);
}
.......
private static UnaryFuntion<Object> ID_FUNC = new UnaryFunction<Object>
{
Object apply(Object arg)
{
return arg;
}
};
public static <T> UnaryFunction<T> idFunction()
{
return (UnaryFunction<T>) ID_FUNC;
}
In main:
public static void main(String[] args)
{
String[] strings = {"Peter", "Paul", "Mary"};
UnaryFunction<String> names = idFunction();
for(String s : strings)
{
System.out.println(names.apply(s));
}
Number[] numbers = {1, 2.0, 3L};
UnaryFunction<Number> nums = idFunction();
for(Number n : numbers)
{
System.out.println(nums.apply(n));
}
}
My question is, why do we need a generic interface here?
Would simply the following suffice:
public interface UnaryFunction
{
Object apply(Object arg); //Object as return type and argument type, instead.
}
? What is the need here to use generics?
And, what is actually a generic singleton factory? What is it good for?
Thanks.
The generic singleton factory is the idFunction in your example. Without it you would have a choice between two ugly alternatives, either require a cast wherever you use it, like this:
public class ExampleWithoutGenericSingletonFactory {
static UnaryFunction<Object> ID_FUNC = new UnaryFunction<Object>() {
public Object apply(Object arg) {
return arg;
}
};
public static void main(String[] args) {
BigDecimal b = new BigDecimal("1234.1241234");
BigDecimal b1 = (BigDecimal)(ID_FUNC.apply(b)); // have to cast here >_<
System.out.println("engineeringstring val of b1 = "
+ b1.toEngineeringString());
}
}
or make separate implementations for every type you want to support:
public static UnaryFunction<String> ID_FUNC_STRING = new UnaryFunction<String>() {
public String apply(String arg) {
return arg;
}
};
public static UnaryFunction<Number> ID_FUNC_NUM = new UnaryFunction<Number>() {
public Number apply(Number arg) {
return arg;
}
};
public static UnaryFunction<BigDecimal> ID_FUNC_DECIMAL = new UnaryFunction<BigDecimal>() {
public Number apply(BigDecimal arg) {
return arg;
}
};
giving you some ugly verbose cut-n-pasted code with a different name for every type that you have to keep straight. But since you know it's a pure function and the types get erased, you can have only one implementation (ID_FUNC) and have the singleton factory idFunction return it.
You would use this for cases where you have one function implementation that you want to be able to specify different types on, where the implementation is stateless.
The example could be better, since it only calls toString on the objects returned from the function call there's no demonstrated benefit from the factory. If the example showed using type-specific methods on the objects returned then the benefit might be more apparent.
An unchecked cast warning comes up when you do this, but it's safe to suppress it (which is what Joshua Bloch advises).

Generic static factory

I am getting a compilation error. I want my static method here to return a factory that creates and return Event<T> object. How can I fix this?
import com.lmax.disruptor.EventFactory;
public final class Event<T> {
private T event;
public T getEvent() {
return event;
}
public void setEvent(final T event) {
this.event = event;
}
public final static EventFactory<Event<T>> EVENT_FACTORY = new EventFactory<Event<T>>() {
public Event<T> newInstance() {
return new Event<T>();
}
};
}
Generic parameters of a class do not apply to static members.
The obvious solution is to use a method rather than a variable.
public static <U> EventFactory<Event<U>> factory() {
return new EventFactory<Event<U>>() {
public Event<U> newInstance() {
return new Event<U>();
}
};
}
The syntax is more concise in the current version of Java.
It is possible to use a the same instance of EventFactory stored in a static field, but that requires an unsafe cast.
You have:
public final class Event<T> {
...
public final static EventFactory<Event<T>> EVENT_FACTORY = ...
}
You cannot do this. T is a type that is associated with a specific instance of an Event<T>, and you cannot use it in a static context.
It's hard to give you good alternate options without knowing more about what exactly you are trying to do, as this is sort of an odd-looking factory implementation. I suppose you could do something like (put it in a method instead):
public final class Event<T> {
...
public static <U> EventFactory<Event<U>> createEventFactory () {
return new EventFactory<Event<U>>() {
public Event<U> newInstance() {
return new Event<U>();
}
};
};
}
And invoke it like:
EventFactory<Event<Integer>> factory = Event.<Integer>createEventFactory();
Or, if you don't want to be explicit (you don't really need to be, here):
EventFactory<Event<Integer>> factory = Event.createEventFactory();
Why don't you get rid of the whole static member of Event thing and either keep the factories separate, e.g.:
public final class GenericEventFactory<T> extends EventFactory<Event<T>> {
#Override public Event<T> newInstance() {
return new Event<T>();
}
}
And use, e.g., new GenericEventFactory<Integer>() where appropriate?

How to display a name/label for each Parameterized Junit test in Eclipse [duplicate]

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

Changing names of parameterized tests

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

Categories

Resources