Arn*_*ost 4 java reflection mockito
我们正在使用Mock-Factory为我们的开发人员提供关于模拟功能的最大舒适度,以及对mockito本身不太可能需要的专业知识.
为此,我们的Mock-Factory提供了一个方法来创建一个模拟给定类名,方法名(通过regexp)和给定的返回值,它看起来如下(清楚这个问题的相关部分) ):
public <T> T getMockForMethod(Class<T> clazz, String methodName, Object methodResponse)
{
T mockForMethod = mock(clazz);
for (Method m : clazz.getDeclaredMethods ())
{
if (m.getName ().matches (methodName) &&
m.getReturnType ().isAssignableFrom (methodResponse.getClass ()))
{
try
{
Class<?>[] paramTypes = m.getParameterTypes ();
Object[] params = new Object[paramTypes.length];
for (Object o : params)
{
o = Mockito.anyObject ();
}
Mockito.when (m.invoke (mockForService, params)).thenReturn (methodResponse);
}
catch (IllegalArgumentException e)
{
e.printStackTrace (System.err);
}
catch (IllegalAccessException e)
{
e.printStackTrace (System.err);
}
catch (InvocationTargetException e)
{
e.printStackTrace (System.err);
}
}
}
return mockForMethod;
}
Run Code Online (Sandbox Code Playgroud)
正如您所见,方法名称与名称(regexp)和正确的给定返回类型匹配.
它工作正常,但我有点困扰我必须建立人工参数阵列的事实params!不,这种方法
Mockito.when (m.invoke (mockForService, Mockito.anyVararg ())).thenReturn(methodResponse);
Run Code Online (Sandbox Code Playgroud)
没用!但我真的不明白为什么!?
任何人都可以给我上述代码的原因或更好的替代方案吗?
JB *_*zet 15
你不应该这样做.Mockito是一个设计精良,易于学习,记录极为明确,几乎是事实上的标准框架.它是类型安全的,不需要反射,这使得测试易于阅读和理解.
让您的开发人员学习真正的Mockito并直接使用其API.他们会很乐意使用它,因为它会比你自己的超级api更好,更容易使用和更灵活的设计,并且他们会知道他们不会因为他们可能会使用它而无所学习Mockito在其他项目甚至其他工作.
Mockito不需要其他专有API.因此,我建议的替代方案是忘记这一点,并向您的开发人员讲授Mockito.
那么你的方法并不是一个好的方法,它通常是过度工程的开发人员.即使你的团队是"幼崽",也不像他们在使用Mockito时必须编写ASM.此外,如果你这样做,你可以避免Mockito提供的简单,表现力或可插拔性的所有好处.作为一名建筑师,我宁愿确保我的工程师了解他们正在做什么而不是把它们放在婴儿公园里.他们怎么能成为一个伟大的团队呢?
此处提供的实现可能过于简单化,无法支持处理反射,桥接方法,变量,覆盖等时可能遇到的所有情况.如果此代码失败,则无法理解消息.简而言之,您可以直接使用Mockito的所有好处,并且无论如何都不需要添加项目.
但是,为了回答你的问题,那里发生了什么.仔细看看你的代码,似乎你不想关心传递给方法的args.
所以假设你在被模拟的类中有以下实际方法:
String log2(String arg1, String arg2)
Run Code Online (Sandbox Code Playgroud)
和
String log1N(String arg1, String... argn)
Run Code Online (Sandbox Code Playgroud)
现在编译器看到了什么,第一个log2采用2参数类型String的方法和一个log1N采用2参数的方法,一个是类型String,另一个是类型String[](变量参数由编译器转换为数组).
如果直接在这些方法上使用Mockito,您将编写以下内容.
given(mock.log2("a", "b")).will(...);
given(mock.log1N("a", "b", "c", "d")).will(...);
Run Code Online (Sandbox Code Playgroud)
你logN("a", "b", "c", "d")就像普通的java一样写.当你想使用参数匹配器时,你将使用2 arg方法编写它:
given(mock.log2(anyString(), anyString())).will(...);
Run Code Online (Sandbox Code Playgroud)
现在使用vararg方法:
given(mock.log1N(anyString(), anyString(), anyString())).will(...); // with standard arg matchers
given(mock.log1N(anyString(), Mockito.<String>anyVararg())).will(...); // with var arg matcher
Run Code Online (Sandbox Code Playgroud)
在第一种情况下,Mockito足够聪明地理解最后两个参数匹配,必须进入最后一个vararg,即argn,所以Mockito理解这个方法将匹配,如果只有3个参数(varargs被flatened)在第二种情况下anyVararg指示mockito,可能有任何参数计数.
现在,回到反射代码,签名Method.invoke是:
public Object invoke(Object obj, Object... args)
Run Code Online (Sandbox Code Playgroud)
传递真实参数时反射和变量的典型用法是:
log2_method.invoke(mock, "a", "b");
log1N_method.invoke(mock, "a", new String[] { "b", "c", "d" });
Run Code Online (Sandbox Code Playgroud)
或者因为这个invoke方法基于vararg,它可以像这样写:
log1N_method.invoke(mock, new Object[] {"a", new String[] { "b", "c", "d" }});
Run Code Online (Sandbox Code Playgroud)
因此,在调用中传递的参数vararg数组必须实际匹配被调用方法的签名.
这个调用当然会失败,然后是log1N_method.invoke(mock,"a","b","c","d");
因此,当您尝试使用此行代码时anyVararg,调用不遵循被调用方法参数的签名:
Mockito.when (m.invoke(mockForMethod, Mockito.anyVararg())).thenReturn(methodResponse);
Run Code Online (Sandbox Code Playgroud)
它只有在方法m只有一个参数时才有效.然而,您必须将其转换为数组内部的反射API(因为vararg实际上是数组).这里的诀窍是vararg invoke(Object obj, Object... args)与被调用的方法vararg混淆.
所以在我的例子中使用arg匹配器你应该这样做:
when(
log1N.invoke(mock, anyString(), new String[] { Mockito.<String>anyVararg() })
).thenReturn("yay");
Run Code Online (Sandbox Code Playgroud)
因此,如果只有一个参数是vararg,那就是同样的事情:
String log1(String... argn)
when(
logN.invoke(mock, new String[] { Mockito.<String>anyVararg() })
).thenReturn("yay");
Run Code Online (Sandbox Code Playgroud)
当然,您不能anyVararg在非vararg方法上使用,因为签名中的参数布局将不匹配.
正如你在这里看到的,如果你采用这种方式将Mockito抽象给你的团队,你将不得不管理很多班级怪异.我不是说这是不可能的.但作为此代码的所有者,您必须维护它,修复它,并考虑可能出错的事情,并使这个抽象代码的用户理解它.
抱歉感到如此咄咄逼人,这对我来说似乎是错的,我强调这些警告.
| 归档时间: |
|
| 查看次数: |
12276 次 |
| 最近记录: |