在多线程环境中使用模拟对象
Using mock objects in multithreaded environment
从 jMock 2.6 开始,我可以通过
确保多个线程一致地看到我的模拟对象
final Mockery mockery = new Mockery();
mockery.setThreadingPolicy(new Synchroniser());
在使用 jMock 2.5 时,我有哪些选择(我正在经历间歇性测试 "flakes")?
特别是,是否足以(更新:不,不符合期望)包装所有模拟对象方法调用使用 synchronized
?
<T> T synchronizedMock(final T mock,
final Class<T> clazz) {
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class[] {clazz, CaptureControl.class},
(proxy, method, args) -> {
synchronized (mock) {
return method.invoke(mock, args);
}
});
}
使用上述方法时,我 运行 陷入任何死锁的机会有多大?
您是否考虑过使用 CGLib + ObjenesisHelper? CGLib 将允许您代理 classes 和接口,而不仅仅是像 java.lang.reflect.Proxy
这样的接口,而 ObjenesisHelper 将允许您构造 class 的实例,而无需调用构造函数。
参见 here for a CGLib example and here for a ObjenesisHelper example。
此外,您可以解压缩 InvocationTargetException
以确保代理实例抛出由模拟 class 定义的预期 Exception
。最后,使用 registerStaticCallbacks
将确保绑定的方法拦截器存在于所有调用线程中。
public <T> T createProxy(final Class<? extends T> classToMock, final T mock) {
final MethodInterceptor interceptor = (object, method, args, proxy) -> {
synchronized (mock) {
try {
return method.invoke(mock, args);
} catch (final InvocationTargetException e) {
if (e.getCause() != null) {
throw e.getCause();
}
throw e;
}
}
};
final Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(classToMock);
final Set<Class<?>> interfaces = new LinkedHashSet<>();
if (classToMock.isInterface()) {
interfaces.add(classToMock);
}
interfaces.addAll(Arrays.asList(classToMock.getInterfaces()));
interfaces.add(CaptureControl.class);
enhancer.setInterfaces(interfaces.toArray(new Class[interfaces.size()]));
enhancer.setCallbackType(interceptor.getClass());
final Class<?> proxyClass = enhancer.createClass();
Enhancer.registerStaticCallbacks(proxyClass, new Callback[] { interceptor });
return (T) ObjenesisHelper.newInstance(proxyClass);
}
When using the above approach, what are my chances to run into any deadlocks?
我不相信你提供的解决方案,上面建议的解决方案也不应该 运行 进入任何死锁 (假设你的代码中没有死锁). synchronized
的使用将确保在任何给定时间只有一个线程可以操作模拟实例。除非 jmock 将方法调用委托给单独的线程 (据我所知它没有) 那么代码应该可以正常执行而不是阻塞。如果 jmock 要求您一次锁定所有 Mockery
个实例,那么您可以传入一个专用对象进行同步,或者为所有代理拦截器提供可重入锁以共享。
从 jMock 2.6 开始,我可以通过
确保多个线程一致地看到我的模拟对象final Mockery mockery = new Mockery();
mockery.setThreadingPolicy(new Synchroniser());
在使用 jMock 2.5 时,我有哪些选择(我正在经历间歇性测试 "flakes")?
特别是,是否足以(更新:不,不符合期望)包装所有模拟对象方法调用使用 synchronized
?
<T> T synchronizedMock(final T mock,
final Class<T> clazz) {
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class[] {clazz, CaptureControl.class},
(proxy, method, args) -> {
synchronized (mock) {
return method.invoke(mock, args);
}
});
}
使用上述方法时,我 运行 陷入任何死锁的机会有多大?
您是否考虑过使用 CGLib + ObjenesisHelper? CGLib 将允许您代理 classes 和接口,而不仅仅是像 java.lang.reflect.Proxy
这样的接口,而 ObjenesisHelper 将允许您构造 class 的实例,而无需调用构造函数。
参见 here for a CGLib example and here for a ObjenesisHelper example。
此外,您可以解压缩 InvocationTargetException
以确保代理实例抛出由模拟 class 定义的预期 Exception
。最后,使用 registerStaticCallbacks
将确保绑定的方法拦截器存在于所有调用线程中。
public <T> T createProxy(final Class<? extends T> classToMock, final T mock) {
final MethodInterceptor interceptor = (object, method, args, proxy) -> {
synchronized (mock) {
try {
return method.invoke(mock, args);
} catch (final InvocationTargetException e) {
if (e.getCause() != null) {
throw e.getCause();
}
throw e;
}
}
};
final Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(classToMock);
final Set<Class<?>> interfaces = new LinkedHashSet<>();
if (classToMock.isInterface()) {
interfaces.add(classToMock);
}
interfaces.addAll(Arrays.asList(classToMock.getInterfaces()));
interfaces.add(CaptureControl.class);
enhancer.setInterfaces(interfaces.toArray(new Class[interfaces.size()]));
enhancer.setCallbackType(interceptor.getClass());
final Class<?> proxyClass = enhancer.createClass();
Enhancer.registerStaticCallbacks(proxyClass, new Callback[] { interceptor });
return (T) ObjenesisHelper.newInstance(proxyClass);
}
When using the above approach, what are my chances to run into any deadlocks?
我不相信你提供的解决方案,上面建议的解决方案也不应该 运行 进入任何死锁 (假设你的代码中没有死锁). synchronized
的使用将确保在任何给定时间只有一个线程可以操作模拟实例。除非 jmock 将方法调用委托给单独的线程 (据我所知它没有) 那么代码应该可以正常执行而不是阻塞。如果 jmock 要求您一次锁定所有 Mockery
个实例,那么您可以传入一个专用对象进行同步,或者为所有代理拦截器提供可重入锁以共享。