I've got the following class with three public static methods
:
package unittests;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestMethodsClass
{
// Test method to run a private void Method from a class
public static void runPrivateVoidMethod(Object ob, String methodName, Class<?>[] parameters){
try {
Method method = null;
if(parameters == null){
Class<?>[] nullParameter = (Class[])null;
method = ob.getClass().getDeclaredMethod(methodName, nullParameter);
}
else
method = ob.getClass().getDeclaredMethod(methodName, parameters);
if(method != null){
method.setAccessible(true);
method.invoke(ob);
}
}
catch (NoSuchMethodException ex){
ex.printStackTrace();
}
catch (IllegalAccessException ex){
ex.printStackTrace();
}
catch (IllegalArgumentException ex){
ex.printStackTrace();
}
catch (InvocationTargetException ex) {
ex.printStackTrace();
}
}
// Test method to run a private Method that returns something from a class
public static Object runPrivateReturnMethod(Object ob, String methodName, Class<?>[] parameters){
Object returnObject = null;
try {
Method method = null;
if(parameters == null){
Class<?>[] nullParameter = (Class[])null;
method = ob.getClass().getDeclaredMethod(methodName, nullParameter);
}
else
method = ob.getClass().getDeclaredMethod(methodName, parameters);
if(method != null){
method.setAccessible(true);
returnObject = method.invoke(ob);
}
}
catch (NoSuchMethodException ex){
ex.printStackTrace();
}
catch (IllegalAccessException ex){
ex.printStackTrace();
}
catch (IllegalArgumentException ex){
ex.printStackTrace();
}
catch (InvocationTargetException ex) {
ex.printStackTrace();
}
return returnObject;
}
// Test method to access a private Field from a class
public static void setPrivateField(Object ob, String fieldName, Object value){
try {
Field field = ob.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(ob, value);
}
catch (NoSuchFieldException ex){
ex.printStackTrace();
}
catch (IllegalAccessException ex){
ex.printStackTrace();
}
catch (IllegalArgumentException ex){
ex.printStackTrace();
}
}
}
The purpose of these methods is to be able to call private methods
or set private fields
in MyObjectInstance from within a UnitTest-class
. Some examples:
// public method equivalent-call: myObjectInstance.doSomething();
TestMethodsClass.runPrivateVoidMethod(myObjectInstance, "doSomething", null);
// public method equivalent-call: myObjectInstance.doSomething(String s);
TestMethodsClass.runPrivateVoidMethod(myObjectInstance, "doSomething", ?¿?¿?¿);
// public method equivalent-call: boolean b = myObjectInstance.doSomethingWithBooleanReturn();
boolean b = (boolean)TestMethodsClass.runPrivateReturnMethod(myObjectInstance, "doSomethingWithBooleanReturn", null);
// public method equivalent-call: String s = myObjectInstance.doSomethingWithStringReturn();
String s = (String)TestMethodsClass.runPrivateReturnMethod(myObjectInstance, "doSomethingWithStringReturn", null);
// public method equivalent-call: MyOtherObject moj = myObjectInstance.doSomethingWithMyOtherObjectReturn();
MyOtherObject moj = (MyOtherObject)TestMethodsClass.runPrivateReturnMethod(myObjectInstance, "doSomethingWithMyOtherObjectReturn", null);
// public method equivalent-call: boolean b = myObjectInstance.doSomethingWithBooleanReturn(String s);
boolean b = TestMethodsClass.runPrivateReturnMethod(myObjectInstance, "doSomethingWithMyOtherObjectReturn", ?¿?¿?¿);
// private boolean b;
// In-Object public field equivalent-set: b = true;
TestMethodsClass.setPrivateField(myObjectInstance, "b", true);
// private String s;
// In-Object public field equivalent-set: s = "a string";
TestMethodsClass.setPrivateField(myObjectInstance, "s", "a string");
Everything works as I want, except for one thing: How do I put the parameters
in? So, what should I replace ?¿?¿?¿
with in the examples above? (And how should I change my public static methods
to be able to use the parameters
?)
I did try the following parameters
so far (with no result). Some are not giving errors, but aren't being set (I've used set methods to test the parameters
) and some are giving errors (like the int
):
// This gives no errors, but doesn't work
new Class<?>[]{ myOtherObjectInstance.getClass() }) // <- parameter
// Error: The method runPrivateVoidMethod(Object, String, Class<?>[]) in the type TestMethodsClass is not applicable for the arguments (MyOtherObjectInstance, String, int)
int i = 5;
i // <- parameter
// Error: Type mismatch: cannot convert from int to Class<?>
int i = 5;
new Class<?>[]{ i } // <- parameter
// Error: Type mismatch: cannot convert from int to Class<?>
int i = 5;
new Class<?>[]{ (Class<?>)i } <- parameter
// This gives no errors,
int[] iArray = new int[1];
iArray[0] = 5;
new Class<?>[]{ array.getClass() } // <- parameter
Preferably I just want to put something in (like an int
, String
, MyOtherObjectInstance
, int[]
, etc. and cast/convert
those parameters
in the public static methods
to useable Class<?>[]
parameters.
EDIT 1:
Sanjeev's solution was very promising, but still doesn't work. Here are the changes to the methods:
// Test method to run a private void Method from a class
public static void runPrivateVoidMethod(Object ob, String methodName, Class<?>[] paramTypes, Object[] paramValues){
try {
Method method = null;
if(paramTypes == null){
method = ob.getClass().getDeclaredMethod(methodName, (Class[])null);
if(method != null){
method.setAccessible(true);
method.invoke(ob);
}
}
else{
if(paramValues != null && paramTypes.length == paramValues.length){
// TODO: Check if the paramTypes are in the same order as the paramValues
method = ob.getClass().getDeclaredMethod(methodName, paramTypes);
if(method != null){
method.setAccessible(true);
method.invoke(ob, paramValues);
}
}
else
runPrivateReturnMethod(ob, methodName, null, null);
}
}
catch (NoSuchMethodException ex){
ex.printStackTrace();
}
catch (IllegalAccessException ex){
ex.printStackTrace();
}
catch (IllegalArgumentException ex){
ex.printStackTrace();
}
catch (InvocationTargetException ex) {
ex.printStackTrace();
}
}
// Test method to run a private Method that returns something from a class
public static Object runPrivateReturnMethod(Object ob, String methodName, Class<?>[] paramTypes, Object[] paramValues){
Object returnObject = null;
try {
Method method = null;
if(paramTypes == null){
method = ob.getClass().getDeclaredMethod(methodName, (Class[])null);
if(method != null){
method.setAccessible(true);
returnObject = method.invoke(ob);
}
}
else{
if(paramValues != null && paramTypes.length == paramValues.length){
// TODO: Check if the paramTypes are in the same order as the paramValues
method = ob.getClass().getDeclaredMethod(methodName, paramTypes);
if(method != null){
method.setAccessible(true);
returnObject = method.invoke(ob, paramValues);
}
}
else
returnObject = runPrivateReturnMethod(ob, methodName, null, null);
}
}
catch (NoSuchMethodException ex){
ex.printStackTrace();
}
catch (IllegalAccessException ex){
ex.printStackTrace();
}
catch (IllegalArgumentException ex){
ex.printStackTrace();
}
catch (InvocationTargetException ex) {
ex.printStackTrace();
}
return returnObject;
}
And here is the UnitTest that I use:
public void testOrderedProductList(){
// Arrange
int amount = 6;
// First product used in Constructor
Product product1 = new Product();
product1.setProductId(54);
...
OrderedProduct orderedProduct = new OrderedProduct(product1, amount);
// Second product used in the setProduct method
Product product2 = new Product();
product2.setProductId(12);
// Invoke
// HERE IS THE CALL TO THE runPrivateVoidMethod
TestMethodsClass.runPrivateVoidMethod(orderedProduct, "setProduct", new Class<?>[]{ Product.class }, new Object[]{ product2 });
Product p = orderedProduct.getProduct();
...
// Assert
//assertNotNull("product should not be null", p);
assertTrue("product should be a Product-instance", p instanceof Product);
assertEquals("product should equal the set product", product2, p);
...
}
Which fails at: assertEquals("product should equal the set product", product2, p);
(Expected <Product {ProductId=12, ... }>
but was <Product {ProductId=54, ... }>
At request of Sanjeev's; The Product and OrderedProduct classes:
package models;
import business.V;
import android.util.Log;
public class Product
{
private int ProductId;
private String Name;
private int CategoryId;
private double Price;
private boolean Visible;
private int check_option;
public Product(){
check_option = 0;
}
// Overriding this class' toString method for print-out purposes
@Override
public String toString(){
return "Product {" +
"ProductId=" + ProductId + ", " +
"Name=" + Name + ", " +
"CategoryId=" + CategoryId + ", " +
"Price=" + Price + ", " +
"Visible=" + Visible + ", " +
"check_option=" + check_option +
"}";
}
// Getter and Setter of the ProductId
public void setProductId(int id){
if(id > 0)
ProductId = id;
else
ProductId = 0;
}
public int getProductId(){
return ProductId;
}
// Getter and Setter of the Name
public void setName(String n){
if(V.notNull(n, true))
Name = n;
else
Name = null;
}
public String getName(){
return Name;
}
// Getter and Setter of the CategoryId
public void setCategoryId(int id){
if(id > 0)
CategoryId = id;
else
CategoryId = 0;
}
public int getCategoryId(){
return CategoryId;
}
// Getter and Setter of the Price
public void setPrice(double p){
if(p > 0.00)
Price = p;
else
p = 0.00;
}
public double getPrice(){
return Price;
}
// Getter and Setter of the Visible
public void setVisible(boolean v){
Visible = v;
}
public boolean getVisible(){
return Visible;
}
// Getter and Setter of the CheckOption
public void setCheckOption(int o){
Log.i("PRODUCT CHECK", "TEST - Product (" + ProductId + ") check option changed from " + check_option + " to " + o);
if(o >= 0 && o < Config.NUMBER_OF_CHECK_OPTIONS)
check_option = o;
else
check_option = 0;
}
public int getCheckOption(){
return check_option;
}
}
package models;
public class OrderedProduct
{
private Product Product;
private int Amount;
public OrderedProduct(Product p, int a){
setProduct(p);
setAmount(a);
}
// Overriding this class' toString method for print-out purposes
@Override
public String toString(){
return "OrderedProduct {" +
"Product=" + Product + ", " +
"Amount=" + Amount +
"}";
}
// Getter and Setter of the Product
// (The Setter is private since we only use it in the Constructor)
private void setProduct(Product p){
Product = p;
}
public Product getProduct(){
return Product;
}
// Getter and Setter of the Amount
public void setAmount(int a){
if(a >= 0)
Amount = a;
else
Amount = 0;
}
public int getAmount(){
return Amount;
}
}
Thanks in advance for the responses.
You need another parameter to pass in your object values to the invoked method.
So with that said you need to add another parameter to your runPrivateVoidMethod with type as Object[]
public static void runPrivateVoidMethod(Object ob, String methodName, Class<?>[] paramTypes, Object[] paramvalues)
NOTE: size of paramType and paramValues must match and paramValues must contain the value at specified index with the same type as defined on same index in paramTypes.
get the method using
method = ob.getClass().getDeclaredMethod(methodName, paramTypes);
Then use Method#invoke(Object obj,Object... args) to pass your parameters to invoked method.
method.invoke(ob,paramValues);
Hope this gives you some idea.