This question already has answers here:
Running the same JUnit test case multiple time with different data
(9 answers)
Closed 9 years ago.
I need to test a method, eg:
public ObjRet foo(string input)
{
...
return objRet;
}
For the above method, one test case involves input = {null, " ", "", etc} which would return the same default object and there are other test cases
Do I need to have separate test cases for each input (even though I need to validate for same default object)
or can i do something like this,
#Test
void testing()
{
String[] inputs = {null, "", " "};
List<ObjRet> received = new List<ObjRet>();
for(string input : inputs)
received.add(foo(input));
for(ObjRet obj : received)
Assert.assertEquals(obj, default);
}
I just want to make sure that if its a good practice to use for loops for assertions
The JUnitParams library is the perfect way to create the type of parameterized test you're describing. I include it by default with every project because this pattern is so common. See http://code.google.com/p/junitparams/ for the complete documentation (it's so easy it all fits on one page).
With JUnitParams, each String included in the String[] passed to the #Parameters annotation is parsed as a CSV and is split on commas, then leading and trailing whitespace is removed from each token. The number of tokens must also match the number of arguments to the test method. Since you need to include a null, an empty string and a string that consists only of whitespace, you'll need to use a method to provide your parameters as follows:
private static final Object DEFAULT_VALUE = new String("It works!");
private static final Object OTHER_VALUE = new String("It's broken!");
private Object foo(String input) {
Object output = DEFAULT_VALUE;
if(input != null && !"".equals(input.trim())) {
output = OTHER_VALUE;
}
return output;
}
#SuppressWarnings("unused")
private Object[] parameters() {
return $(
new Object[] { null },
new Object[] { "" },
new Object[] { " " }
// ,"Other value"
);
}
#Test
#Parameters(method = "parameters")
public void testing(String input) {
Object obj = foo(input);
assertEquals(obj, DEFAULT_VALUE);
}
The three tests defined in your original question will pass with this example code, but if you uncomment the fourth value ("Other value"), then fourth test will (properly) fail.
Related
I have my application.properties:
test.md5.params=something1,something4
In my java class I am getting this particular value :
and need to create same strings as present in the property file, such as
public String calculate(RequestClass request)
{
List<String> params= //I am getting the values from application.prop
**(above part id done)**
My Question is below ::
now in my params list I have [something1,something4]
so I need to concatenate both the String values like below:
String finalString=request.getSomething1()+request.getSomething4();
return finalString;
}
My Question is how to do this dynamically and in my properties file I might receive "n" of something values.
Note : I need to make the code such that my class remains constant, if in future I am adding 10 more values in properties files, my final string should be returning like
String finalString=request.getSomething1()+request.getSomething4()+....all the values.;
Through reflection this is possible, below is one implementation.
public String calculate(RequestClass request) throws InvocationTargetException, IllegalAccessException {
List<String> params = Arrays.asList("something1", "something4");
// Do your logic to get the method Names from params, below is an simple example - paramsUpdated
List<String> paramsUpdated = Arrays.asList("getSomething1", "getSomething4");
// Reflection to get the methods of request class
Method[] methods = request.getClass().getMethods();
StringBuilder sb = new StringBuilder();
for (String param : paramsUpdated) {
for (Method method : methods) {
if (param.equals(method.getName())) {
sb.append(method.invoke(request));
}
}
}
return sb.toString();
}
is it possible to create a class and have a String ... attribute that takes as many or as little strings as you put?
example:
please excuse my rough pseudocode, this is for java.
//this is the method:
public void getXXXX(String ...) {
//random code executes in a loop with as many as strings that are inputted
}
//this code calls it
getXXXX("Benjamin","Jordan","Steve")
getXXXX("Pengu","No")
getXXXX("hi")
Yes, what you entered will more or less work, you just need a parameter name after your type.
class StringDecorator {
public static String join(final String... strings) {
final var builder = new StringBuilder();
for (final var string : strings) {
builder.append(string);
}
return builder.toString();
}
}
Then invoke this somewhere
StringDecorator.join("Hello, ", "World!"); // "Hello, World!"
This question already has answers here:
Java: Check if enum contains a given string?
(32 answers)
Closed 3 years ago.
I need to find if given String is not in the list of ENUMs.
These Strings come back with spaces, i.e.: "CHILD CARE", "CREDIT CARDS", etc...
Any other ExpenseType should be mapped to OTHER, except HOA. HOA should be completely ignored.
My ENUMs are as follows:
public enum ExpenseType {
AUTOLOAN("AUTO LOAN"),
ALIMONY("ALIMONY"),
CHILDCARE("CHILD CARE"),
CREDITCARDS("CREDIT CARDS"),
INSTALLMENTLOANS("INSTALLMENT LOANS"),
FOOD("FOOD"),
UTILITIES("UTILITIES"),
TRANSPORTATION("TRANSPORTATION"),
OTHER("OTHER");
private String expenseType;
ExpenseType(String expenseType) {
this.expenseType = expenseType;
}
#Override public String toString() {
return this.expenseType;
}
}
The way I am doing this now is as follows:
String expenseDescription = expense.getExpenseDesc().replaceAll(" ", "");
if(EnumUtils.isValidEnum(ExpenseType.class, expenseDescription)) {
monthlyExpenses.setType(ExpenseType.valueOf(expenseDescription).toString());
}
else if(!expenseDescription.equals("HOA")) {
monthlyExpenses.setType(ExpenseType.OTHER.toString());
}
Does anyone know a better way to do this?
Why not use getEnum to get Enum if applicable (check for null or use Optional
if needed)
ExpenseType monthlyExpenses = EnumUtils.getEnum(ExpenseType.class, expenseDescription);
Gets the enum for the class, returning null if not found.
This method differs from Enum.valueOf(java.lang.Class, java.lang.String) in that it does not throw an exception for an invalid enum name.
Also prefer adding to enum a code (String) as a reference, which won't contains spaces and special characters, e.g.
//...
CHILDCARE("CHILD_CARE","CHILD CARE"),
//...
private String expenseType;
private String expenseTypeCode;
ExpenseType(String expenseType, String expenseTypeCode) {
this.expenseType = expenseType;
this.expenseTypeCode = expenseTypeCode;
}
Here is another alternative.
Map<String, String> codes = Arrays.stream(ExpenseType.values()).collect(
Collectors.toMap(ExpenseType::toString, ExpenseType::name));
codes.put("HOA","TBD");
String[] submittedCodes = { "CREDIT CARDS", "FOOD", "UTILITIES", "UNKNOWN"
};
for (String c : submittedCodes) {
String expenseType = codes.getOrDefault(c,"OTHER");
System.out.println(expenseType);
}
First, I didn't see a reason to remove the spaces from the submitted code unless you are concerned about addition of extra spaces creeping in. In that case you should probably also remove tabs.
Since you are already using the enum value to compare to the name, I figured I would just ignore the name as they are effectively the same.
The map is used only to allow a null to be returned in case of a missing key.
The enum was only used to conveniently populate the map.
I did not know exactly how to handle HOA. But all of this is just another alternative for you to possibly investigate as you can rearrange the keys and values, etc to suit your requirements.
Since
public static String requestMethodExecution(String objectName, String className, String methodName, Object...
params) {
return String.format("%s,%s,%s,%s", objectName, className, methodName, Arrays.toString(params));
}
returns a String, and if you would, for example, call the method like this:
requestMethodExecution("foo","bar","fooBar",2.0,3.0,"Hello");
You'd get a String like this: foo,bar,fooBar,[2.0,3.0,Hello]
I would love to iterate over that Array, but I can't since it is a String.
Reason behind this is this method: (I just started with reflection, so I do not know how else to do it)
public static Class[] getParameterType(String ...params) throws ClassNotFoundException {
Class[] paramTypes = new Class[params.length];
for(int i=0; i<params.length;i++){
Class paramClass = Class.forName(params[i]);
if (paramClass == Double.class) {
paramTypes[i] = (double.class);
} else if (paramClass == Integer.class) {
paramTypes[i] = (int.class);
} else {
paramTypes[i] = paramClass;
}
}
return paramTypes;
}
So far I have only come up with a very dirty way:
public static String[] getParams(String message){
int indexOfParamStart = message.indexOf("[");
int indexOfParamEnd = message.indexOf("]")+1;
String[] splitMessage = message.substring(indexOfParamStart, indexOfParamEnd).replaceAll("\\[", "")
.replaceAll("]", "").replaceAll(" ","").split(",");
return splitMessage;
}
Edit: Thanks for looking into this! Since some of you are asking what I am trying to achieve, here is a bit more explanation:
I want to implement a simple request/reply protocol which allows remote method invocation (and I do not want to use java RMI...)
So I listen for requests whose structure can be seen at the requestMethodExecution example.
There I have all the relevant information to call the Method upon my class, so to invoke the method I need it's arguments (and their value) and I do not know how to access them from the given String.
The others are easy with Class c = Class.forName(className); etc..
Edit#2:
My question is not about a simple regex, so why close it? The title already states a different subject, I am getting a bit salty here...
See this this question for using RegEx to extract the array body from the outer string (by the square brackets), and then you can simply use String.split(",") to split the array body into array items.
I'm invoking some method of Class's instance using the method.invoke(instance, args...) way but for each method inside the instance, as the invoke Javadoc rightly points out, each argument must be manually specified.
Thinking about Spring... how it could valorize parameters in controller's method behind the hood during HTTP calls? (but surely it does in a completely different way I think...)
I wonder if there's any way in Java to dynamically pass parameters in reflection (or not even reflection) without specifying each of them singularly.
EDIT
The instance class declaration is something like:
public class Something {
public void doSth(String par1, String par2, Integer par3) {
//....
}
public void doSthElse(String par1, Boolean par2) {
//....
}
public void doSthElseMore(Integer par1) {
//....
}
}
How I'm invoking each method:
...
for (Method method : instance.getDeclaredMethods()) {
Object[] array = //BL: build array of values to pass to the invoke method.
//1. doSth may be new Object[] {"abc", "def", 123}
//2. doSthElse iteration may be new Object[] {"abc", false}
//3. doSthElseMore iteration may be new Object[] {123}
return method.invoke(instance, array);
}
...
As shown above, each method inside Something class (instance) have a different number of parameters.
On each iteration, the array have a different number of values to pass to the invoke.
Actually as #Boris says all I had to do to complete my job was to convert each parameters to the correct type. In this way Java managed to invoke the correct method of the Something class with the correct parameters types.
My project is a Vert.x application using Vavr and jodd but the last return statement shows how I managed to solve.
public Object invokeMethod(Object service, Method method, RoutingContext routingContext) throws Exception {
MultiMap queryParams = routingContext.queryParams();
Map<String, String> pathParams = routingContext.pathParams();
Buffer body = routingContext.getBody();
// 1. type, 2. name, 3. value
List<Tuple3<Class<?>, String, Object>> list = List.empty();
for (Parameter par : method.getParameters()) {
ParamQuery paramQuery = par.getAnnotation(ParamQuery.class);
if (paramQuery != null) {
list = list.push(new Tuple3<Class<?>, String, Object>(par.getType(), paramQuery.value(),
queryParams.get(paramQuery.value())));
}
}
// TypeConverterManager used to "covnert" each object (String) from the HTTP call to the correct data type
return method.invoke(service, list.reverse()
.map(mapper -> TypeConverterManager.lookup(mapper._1()).convert(mapper._3())).toJavaArray());
}
However, this project can be found on GitHub
Since I notice you are using an Integer instead of a int (so no primitives parameters in your examples), you can send null to all your methods without any problems.
So you can create an array of the correct length and this will work in your case.
public static Object[] getParametersArray(Parameter[] param){
Object[] array = new Object[param.length];
// create default primitive values based on param[#].getType()
return array;
}
Then, all you have to do is to iterate the method:
Labo l = new Labo();
for(Method m : Labo.class.getDeclaredMethods()){
if((m.getModifiers() & Modifier.STATIC) > 0){
System.out.println("SKIP " + m.getName());
continue;
}
try {
m.invoke(l, getParametersArray(m.getParameters()));
} catch (Exception e) {
e.printStackTrace();
}
}
Notice the skipped static method, mostly because if you run this in the method containing the main method, you will have a recursive call.
This was tested with :
public void test(String s){
System.out.println("test String " + s);
}
public void test2(String s1, String s2){
System.out.println("test String " + s1 + " | String " + s2);
}
public void test(Integer s){
System.out.println("test Integer " + s);
}
SKIP main
test String null
test Integer null
SKIP getParametersArray
test String null | String null
Note : If you need to manage some primitive values, you will need to get the type of the parameter to provide a default value instead of null