Mockito: Attach Answer to every method of arbitrary Object instance - java

I have the following situation:
I want to attach an Answer to every method call of a specific class instance. So for example with the class
public class Example {
public int example1() { /* */ }
public int example2(Object a) { /* */ }
public int example3(Object a, Integer b) { /* */ }
public int example4(int a) { /* */ }
}
I want to do the following
public Example attachToExample(Example ex) {
Example spy = Mockito.spy(ex);
Answer<Object> answer = /* */;
doAnswer(answer).when(spy).example1();
doAnswer(answer).when(spy).example2(any());
doAnswer(answer).when(spy).example3(any(), any());
doAnswer(answer).when(spy).example4(anyInt());
return spy;
}
This works but what I would like to do is generalize this to not just Example instances but arbitrary Objects.
So what I would like to do is
public Object attachToExample(Object o) {
Object spy = Mockito.spy(o);
Answer<Object> answer = /* */;
for(Method m : o.getClass().getMethods()) {
/* skipping methods that cannot be mocked (equals/hashCode/final/..) */
doAnswer(answer).when(spy)./* Method m with according arguments */;
}
return spy;
}
What I would need to do for that is construct argument matchers any/anyInt/.. depending on the amount of parameters of each method and their types (primitive/non primitive). Ideally I would create a list of arguments like this:
Class<?>[] params = m.getParameterTypes();
ArrayList<Object> args = new ArrayList<>();
for (Class<?> param : params) {
if ("int".equals(param.toString())) {
args.add(ArgumentMatchers.anyInt());
} else { // Cases for other primitive types left out.
args.add(ArgumentMatchers.any()); // Found non primitive. We can use 'any()'
}
}
try {
doAnswer(answer).when(spy).getClass().getMethod(m.getName(), m.getParameterTypes())
.invoke(spy, args.toArray());
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
This does not work as using argument matchers outside of stubbing is not supported but I hope that this makes clear what I want to do.
Is there any way to make this work or is there a different way of archiving what I want to do?

Okay, I have found a way to do what I want:
While the array of arguments cannot be constructed before the invoke call we can do so with an external method call like so:
try {
doAnswer(answer).when(spy).getClass().getMethod(m.getName(), m.getParameterTypes())
.invoke(spy, constructArguments(m));
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
where constructArguments is the following:
private static Object[] getArgumentMatcher(Method m) {
Class<?>[] types = m.getParameterTypes();
Object[] res = new Object[types.length];
for(int i = 0; i < types.length; ++i) {
if (types[i].isPrimitive()) {
// For primitives we need to specify the type explicitly ¯\_(ツ)_/¯
res[i] = any(types[i]);
} else {
res[i] = any();
}
}
return res;
}

Related

How can I test if a Method will accept a parameter type?

Say I have the following code...
#FunctionalInterface
static interface MessageFunction<T> {
void send(T obj);
}
static #interface Message {
Class<?> value();
}
static class Foo {
#Message(String.class)
MessageFunction<String> bass = (string) -> {
// Do Stuff
};
}
static class MessageManager {
Map<Class<?>, MessageFunction<?>> messages = new HashMap<>();
public void register(Object obj) {
for (Field field : obj.getClass().getDeclaredFields()) {
Message message = field.getAnnotation(Message.class);
if (message != null) {
MessageFunction<?> function;
try {
function = (MessageFunction<?>) field.get(obj);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
return;
}
Method sendMethod;
try {
// Will this work?
sendMethod = function.getClass().getDeclaredMethod("send", Object.class);
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
return;
}
// How do I do something like this?
/*if (sendMethod.testParamaters(message.value())) {
this.messages.put(message.value(), function);
}*/
}
}
}
}
public static void main(String[] args) {
MessageManager manager = new MessageManager();
manager.register(new Foo());
}
I am reflecting a field that references an #FunctionalInterface of a generic type. Because the method parameter is also generic I have no way of knowing what parameters it accepts, Thus I must pass it along through other means (the annotation).
The issue is that there is the annotation value and the generic type do not have to match and there seems to be no way to check. I wan't it to fail in registration if the type listed in the annotation would not be accepted into the send method.
How would I go about thing this without actually calling the method. Is there a way? Better yet although I know its most likely impossible, is there a way to know what the parameter type is without the annotation?
The following is just a suggestion, I have used it in my project. But it is not a perfect solution for the question. May be you can download the source of GenericHibernateDao framework and see the sourcecode of method "getTypeArguments". I think it is so cool!.
// get a class object for your entity
Class clazz = ...
Type type = clazz.getGenericSuperclass();
if (type instanceof ParameterizedType) {
Type trueType = ((ParameterizedType)type).getActualTypeArguments()[0];
Class modelClass = (Class) trueType;
// Now you can creat an Instance in you generic parameterType
Object entity = modelClass.forInstance();
}
I do something similar in some of my code Here is a snippet.
Method[] meths = actionClass.getMethods();
for (Method meth : meths) {
Class<?>[] pTypes = meth.getParameterTypes();
/*
* Filter out all methods that do not meet correct
* signature. The correct signature for an action method
* is: String actionName(HttpServletRequest request)
*/
//...check for the correct number of params and the correct param type
if (pTypes.length != 1 || !HttpServletRequest.class.toString().equals(pTypes[0].toString())) {
continue;
} else {
//...check for return type
if (!String.class.toString().equals(meth.getReturnType().toString())) {
continue;
}
}
//If you make it here than that means the method
//meets the requirements to be a full fledged action.
//...
}

Unit testing by passing runtime parameters to java reflect method

I am making a class object using the a java bean in my code. Then I am calling a particular method of that class obj
public static void runUnitTest(String className, String methodName, List<Object> inputParams, Object expectedReturnValue){
try {
// Make the class object to be tested on
Object classObj = Class.forName(className).newInstance();
Method calledMethod = classObj.getClass().getMethod(methodName,inputParams.get(0).getClass());
Object returnVal = calledMethod.invoke(classObj,inputParams.get(0));
}catch (InstantiationException | IllegalAccessException
| ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
I call it this way :
public static void main(String[] args) throws IOException {
List<Object> inputParams = new ArrayList<Object>();
inputParams.add(new BigDecimal(1234));
runUnitTest("NumberTest","getOutputNumber",inputParams,new BigDecimal(5678));
}
The code of NumberTest:
public class NumberTest{
public BigDecimal getOutputNumber(BigDecimal numberId) {
if(numberId.intValue() == 1234)
{
return new BigDecimal(5678);
}else
return new BigDecimal(0);
}
public BigDecimal getAdditionalOutputNumber(BigDecimal numberId, String additionalInfo) {
if(numberId.intValue() == 1234 && "Pass".equals(additionalInfo))
{
return new BigDecimal(5678);
}else
return new BigDecimal(0);
}
}
This works fine as I know that the method getOutputNumber has only one parameter. But when I have to call the same code for multiple methods where number of parameters differ (e.g. getAdditionalOutputNumber) I can't use the same code. I don't want to use a multiple if else or case block on the basis of size of inputParams.
Is there a generic way of calling the below :
Method calledMethod = classObj.getClass().getMethod(methodName,**?? What to pass here ??**);
Object returnVal = calledMethod.invoke(classObj,**?? What to pass here ??**);
You just have to build suitable arrays from the list of parameters to call the reflection API.
Class[] types = new Class[inputParams.size()];
int i = 0;
for(Object param:inputParams) {
types[i++] = param.getClass();
}
Method calledMethod = classObj.getClass().getMethod(methodName,types);
Object returnVal = calledMethod.invoke(classObj,inputParams.toArray());
There might be some issues with primitive types and null values.

Getting method regardless of parameters

I am trying to get method regardless of what parameters that method takes (as of now there is no method overloading and there wouldn't be in future). The only possible solution that i could come up with was
private Method getMethod(Class<?> clas, String methodName) {
try {
Method[] methods = clas.getMethods();
for (Method method : methods) {
if (method.getName().equalsIgnoreCase(methodName)) {
return method;
}
}
} catch (SecurityException e) {
e.printStackTrace();
}
return null;
}
What i want to ask that is there a way to fetch a method regardless of its parameters ? I was looking at clas.getMethod ("methodName", parameters) and if i provide null in there it will try to fetch a method which has no parameters. Which wouldn't be no case.
Any ideas ?
EDIT
Thanks guys for input. In my case, i know that there would be only one method regardless of its case. The reason i am using ignoreCase is because the input will be coming from a developer (in other team) and he will be providing the name as a hard-coded string. So to keep things from spilling out of our hands, I am using a safe approach.
No. The way you've done it is the way to go. A method is identified by its signature and the signature includes the name and the parameter types.
Here is a solution that retrieves all methods with the specified class and method name regardless of the method's parameters:
public class Test
{
private class Foo
{
public void bar()
{
}
public void bar(String s)
{
}
public void goo()
{
}
}
private static Method[] getMethods(Class<?> clazz, String methodName)
{
List<Method> methods = new ArrayList<Method>();
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod: declaredMethods)
{
if (declaredMethod.getName().equals(methodName))
{
methods.add(declaredMethod);
}
}
return methods.toArray(new Method[methods.size()]);
}
public static void main(String[] args)
{
Method[] methods = getMethods(Foo.class, "bar");
System.out.println(Arrays.toString(methods));
}
}
This generates the following output:
[public void com.example.Test$Foo.bar(java.lang.String), public void com.example.Test$Foo.bar()]
You've done just fine. This is basically the same as the solution to a similar problem I dealt with four years ago, creating a means to create callback methods in Java. The constructors for my Callback class were:
public Callback(Class<?> clazz, String methodName, Object parentObj) {
// Find a method with the matching name
Method[] allMethods;
try { allMethods = clazz.getMethods(); }
catch(SecurityException se) { allMethods = new Method[0]; }
int count = 0;
Method single = null;
for(Method m : allMethods) {
if(m.getName().equals(methodName)) {
single = m;
count++;
}
// Can't have more than one instance
if(count > 1)
throw new IllegalArgumentException(clazz.getName()
+ " has more than one method named " + methodName);
}
if(count == 0) // No instances found
throw new IllegalArgumentException(clazz.getName()
+ " has no method named " + methodName);
this.parentObj = parentObj;
this.method = single;
this.parameters = single.getParameterTypes();
}
public Callback(
Class<?> clazz,
String methodName,
Object parentObj,
Class<?>...parameters)
{
try { this.method = clazz.getMethod(methodName, parameters); }
catch(NoSuchMethodException nsme) { nsme.printStackTrace(); }
catch(SecurityException se) { se.printStackTrace(); }
this.parentObj = parentObj;
this.parameters = parameters;
}
My Callback class isn't really useful any more in the era of Java 8, but at the time the only real means for a "callback" in java was anonymous interface implementations, which wasn't sufficient for my use-case.
As you can see in the first constructor, it throws an exception if it finds multiple methods with the same name.
Using java streams there is a really short method of finding a method, the first match, by its name only:
Stream.of(type.getMethods())
.filter((m) -> m.getName().equals(searchedName))
.findFirst()
.get();
I think this is a short and readable possibility in this case.

Java using supertype of several enums

I want to be able to use a supertype over different enums, the code consists of three parts:
Manager.search:
public final List<B> search(final Order order, final Field field, final AbstractConstraint... c) throws SearchException {
if (c.length == 0) {
throw new IllegalArgumentException("orm.Manager.search: c.length == 0");
}
try {
List<B> beans = new ArrayList<>();
for (AbstractConstraint constraint : c) {
try (PreparedStatement ps = new QueryBuilder(connection, tableName(), getPaths(), searchQuery()).add(constraint).order(order, field).build();ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
beans.add(createBean(rs));
}
}
}
return beans;
} catch (SQLException ex) {
Logger.getLogger(Manager.class.getName()).log(Level.SEVERE, null, ex);
throw new SearchException(ex);
}
}
The order and field variables are most important here.
The auto-generated TemplateAttributeField.java:
public enum TemplateAttributeField implements Field {
templateId,
attributeOrder,
attributeName,
x1,
x2;
}
And the calling code:
try (TemplateAttributeManager templateAttributeManager = ManagerFactory.getTemplateAttributeManager()) {
List<TemplateAttributeBean> templateAttributes = null;
try {
templateAttributes = templateAttributeManager.search(Order.ASCENDING, TemplateAttributeField.attributeOrder, new TemplateAttributeConstraint.Builder().templateId(template.getTemplateId()).build());
} catch (SearchException ex) {
Logger.getLogger(OutputProcessor.class.getName()).log(Level.SEVERE, null, ex);
}
for (Word word : words) {
}
}
However at templateAttributes = ... I get the following exception/error:
no suitable method found for search(Order,TemplateAttributeField,TemplateAttributeConstraint)
method Manager.search(Order,Field,AbstractConstraint...) is not applicable
(actual argument TemplateAttributeField cannot be converted to Field by method invocation conversion)
And the Field class is on more than an interface which does not prevent extra functionality.
Am I missing something here, or how should I fix it?
I've tried to create a minimal working example, but it seems to work for me. Could you try if this works for you and is the same as you require?
public class Test {
static interface I {}
static enum E implements I {A}
static void m(I i) {}
public static void main(String[] args) {
m(E.A);
}
}
Also ensure that you implement the same Field interface in your enum as you require in your method. Maybe there's a name clash and you're using interfaces from different packages.

How to bind ServletRequest params to domain objects?

I want to bind parameters of a ServletRequest to arbitrary domain objects. Params are available as a map of type
Map<String, String[]> // String = key, String[] = values
They should be converted and bound to the fields of the target object. Id like to have a method like this:
// definition:
public void bind(Map<String, String[]>, T target)
// usage:
bind(request.getParameterMap(), domainObject);
What is the best way to bind request parameters to a domain object in a Java Servlet? What libs are available for this purpose or how would you write one?
Have you looked into the Spring MVC (http://springsource.org) framework at all? It provides binding functionality which you can use outside of Spring. Also, if you are not currently using another MVC framework, it's a good one to consider.
I'd say it depends on what kind of object the actual domainObject is. If it's a bean, you could use an reflection based automated bean populator such as my BeanPropertyController (it's free so give it a try! License is ASF2.0) to do the following:
/* Assumed parameter input:
* a=hello
* c=123
* e=f,g,h
*
* Matching bean:
*/
public class ParamBean {
private String a;
private int c;
private String[] e;
/* + all the normal getters and setters you'd see in a bean */
}
/* somewhere in your servlet: */
BeanPropertyController bpc = BeanPropertyController.of(ParamBean.class);
for (Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
bpc.mutate(entry.getKey(),
getNiceValue(entry.getValue(), bpc.typeOf(entry.getKey()));
}
ParamBean bean = bpc.getObject(ParamBean.class);
/* somewhere else in the class */
public Object getNiceValue(String[] objs, Class<?> desiredClass) {
if (objs.length == 1) {
Object o = objs[0];
return convertString(o);
} else {
Object[] newObjs = Object[objs.length];
for (int i=0;i<objs.length;i++) {
newObjs[i] = convertString(objs[i]);
}
return (Object) newObjs;
}
}
private Object convertString(String value, Class<> desiredClass) {
if (Number.class.isAssignableFrom(desiredClass)) {
Double d = null;
try {
d = Double.parseDouble(value)
} catch (NumberFormatException e) {
// Not of the desired type, do whatever you want
}
if (Double.class.isAssignableFrom(desiredClass)) {
return d;
} else if (Long.class.isAssignableFrom(desiredClass)) {
return d.longValue();
} else if (Integer.class.isAssignableFrom(desiredClass)) {
return d.intValue();
} // and so on...
} else if (Boolean.class.isAssignableFrom(desiredClass)) {
try {
return Boolean.valueOf(value);
} catch (NullPointerException e) {
// Not of the desired type, do whatever you want
}
} else {
return value; // or whatever your default type is
}
}

Categories