简体   繁体   中英

Using mock objects in multithreaded environment

Starting with jMock 2.6 , I can make sure my mock objects are seen consistently by multiple threads via

final Mockery mockery = new Mockery();
mockery.setThreadingPolicy(new Synchroniser());

What are my options (I'm experiencing intermittent test "flakes") when using jMock 2.5 ?

Particularly, is it sufficient ( update : no, doesn't play well with expectations ) to wrap all mock object method invocations using 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);
                }
            });
}

When using the above approach, what are my chances to run into any deadlocks?

Have you looked into using CGLib + ObjenesisHelper? CGLib will allow you to proxy both classes and interfaces instead of just interfaces like java.lang.reflect.Proxy , and ObjenesisHelper will allow you to construct an instance of a class without having to invoke a constructor. See here for a CGLib example and here for a ObjenesisHelper example .

Additionally you can unpack the InvocationTargetException to ensure that the proxy instance throws the expected Exception defined by the mocked class. Finally, using registerStaticCallbacks will ensure that the bound method interceptor is present among all invoking threads.

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?

I do not believe the solution you provided, nor the suggested solution above should run into any deadlocks (assuming that there are no deadlocks already present in your code) . The use of synchronized will ensure that only one thread will can manipulate the mock instance at any given time.. and unless jmock delegates method invocation to a separate thread (which to my knowledge it does not) then the code should execute normally and not block. If jmock requires you lock on all Mockery instances at once then you could pass in a dedicated object to synchronize on, or provide a Re-entrant lock for all proxy interceptors to share.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM