Reputation: 3207
I'm executing unit tests of a GWT application in plain Java using directly Guice for DI instead of GIN (used in normal execution). I'm also using the GWTEventBinder library (https://github.com/google/gwteventbinder) as support library for event declaration and listening.
As required by the library I'm declaring an EventBinder for each class that listen to the events. During the normal execution the instance of the binder is injected by GIN.
However during the unit tests the instance should be produced by Guice. Any idea on how to make Guice produce the EventBinder instance?
Upvotes: 3
Views: 219
Reputation: 15321
Based on the comments and suggestions from an issue on the gwteventbinder project (which you reported :)), I've come up with the following code:
public class FakeEventBinderProvider implements FakeProvider<EventBinder<?>> {
@Override
public EventBinder<?> getFake(Class<?> type) {
return (EventBinder<?>) Proxy.newProxyInstance(FakeEventBinderProvider.class.getClassLoader(), new Class<?>[] { type }, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, final Object[] args) throws Throwable {
String methodName = method.getName();
assert methodName.equals("bindEventHandlers");
final List<HandlerRegistration> registrations = new LinkedList<HandlerRegistration>();
EventBus eventBus = (EventBus) args[1];
List<Method> presenterMethods = getAllMethods(args[0].getClass());
for (final Method presenterMethod : presenterMethods) {
if (presenterMethod.isAnnotationPresent(EventHandler.class)) {
@SuppressWarnings("unchecked") // Should always be ok, since the Generator for EventBinder should do all the safe-checking
Class<? extends GenericEvent> eventType = (Class<? extends GenericEvent>) (presenterMethod.getParameterTypes())[0];
registrations.add(eventBus.addHandler(GenericEventType.getTypeOf(eventType), new GenericEventHandler() {
@Override
public void handleEvent(GenericEvent event) {
try {
presenterMethod.setAccessible(true);
presenterMethod.invoke(args[0], event);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}));
}
}
return new HandlerRegistration() {
@Override
public void removeHandler() {
for (HandlerRegistration registration : registrations) {
registration.removeHandler();
}
registrations.clear();
}
};
}
});
}
private List<Method> getAllMethods(Class<?> type) {
List<Method> methods = new LinkedList<Method>();
methods.addAll(Arrays.asList(type.getDeclaredMethods()));
if (type.getSuperclass() != null) {
methods.addAll(getAllMethods(type.getSuperclass()));
}
return methods;
}
}
As suggested, I've based this on FakeUiBinderProvider
's implementation.
It's pretty straightforward, once you get rid of the Java Reflection cruft:
@EventHandler
.HandlerRegistration
that on call to removeHandler
removes all the handlers added in the previous point (this behavior is copied from the actual implementation of gwteventbinder).Remember to register this provider, for example in your @Before
method:
@Before
public void setUpEventBindery() {
GwtMockito.useProviderForType(EventBinder.class, new FakeEventBinderProvider());
}
You only need to do this for the base interface, EventBinder
, since as the documentation for GwtMockito.useProviderForType
says:
(..) the given provider should be used to GWT.create instances of the given type and its subclasses.
Upvotes: 1