在多线程环境中使用模拟对象

时间:2018-08-22 11:12:44

标签: java multithreading unit-testing concurrency jmock

jMock 2.6 开始,我可以通过

确保我的模拟对象被多个线程一致地看到
final Mockery mockery = new Mockery();
mockery.setThreadingPolicy(new Synchroniser());

使用 jMock 2.5 时,我有哪些选择(我遇到间歇性测试“剥落”)?

尤其是,使用synchronized来包装所有模拟对象方法调用是否足够(更新:否,不能很好地配合期望使用?)?< / p>

<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);
                }
            });
}

使用上述方法时,我有什么机会陷入僵局?

1 个答案:

答案 0 :(得分:2)

您是否考虑过使用CGLib + ObjenesisHelper? CGLib将允许您代理类和接口,而不仅仅是java.lang.reflect.Proxy之类的接口,并且ObjenesisHelper将允许您构造类的实例而不必调用构造函数。 请参见here for a CGLib examplehere for a ObjenesisHelper example

此外,您可以解压缩InvocationTargetException包以确保代理实例抛出模拟类定义的预期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);
}
  

使用上述方法时,我有什么机会陷入僵局?

我不相信您提供的解决方案,也不建议上面的建议解决方案遇到任何僵局(假设您的代码中不存在僵局)。使用synchronized将确保在任何给定时间只有一个线程可以操纵该模拟实例。.除非jmock将方法调用委托给单独的线程(据我所知不会这样),那么代码应该正常执行而不是阻塞。如果jmock要求您一次锁定所有Mockery实例,则可以传入一个专用对象进行同步,或者提供一个重入锁以供所有代理拦截器共享。