Test simple event cancellation with method references instead of reflection

This commit is contained in:
ljacqu 2016-10-16 11:19:54 +02:00
parent 700ab5f3e4
commit 252813197f
7 changed files with 72 additions and 194 deletions

View File

@ -18,7 +18,7 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static fr.xephi.authme.listener.ListenerTestUtils.checkEventIsCanceledForUnauthed;
import static fr.xephi.authme.listener.EventCancelVerifier.withServiceMock;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Mockito.mock;
@ -41,65 +41,12 @@ public class EntityListenerTest {
@Test
public void shouldHandleSimpleEvents() {
checkEventIsCanceledForUnauthed(listener, listenerService, EntityTargetEvent.class);
checkEventIsCanceledForUnauthed(listener, listenerService, FoodLevelChangeEvent.class);
checkEventIsCanceledForUnauthed(listener, listenerService, EntityShootBowEvent.class);
}
@Test
public void shouldCancelEntityInteractEvent() {
// given
EntityInteractEvent event = mock(EntityInteractEvent.class);
given(listenerService.shouldCancelEvent(event)).willReturn(true);
// when
listener.onLowestEntityInteract(event);
// then
verify(listenerService).shouldCancelEvent(event);
verify(event).setCancelled(true);
}
@Test
public void shouldNotCancelEntityInteractEvent() {
// given
EntityInteractEvent event = mock(EntityInteractEvent.class);
given(listenerService.shouldCancelEvent(event)).willReturn(false);
// when
listener.onLowestEntityInteract(event);
// then
verify(listenerService).shouldCancelEvent(event);
verifyZeroInteractions(event);
}
@Test
public void shouldCancelEntityInteractEventHighest() {
// given
EntityInteractEvent event = mock(EntityInteractEvent.class);
given(listenerService.shouldCancelEvent(event)).willReturn(true);
// when
listener.onEntityInteract(event);
// then
verify(listenerService).shouldCancelEvent(event);
verify(event).setCancelled(true);
}
@Test
public void shouldNotCancelEntityInteractEventHighest() {
// given
EntityInteractEvent event = mock(EntityInteractEvent.class);
given(listenerService.shouldCancelEvent(event)).willReturn(false);
// when
listener.onEntityInteract(event);
// then
verify(listenerService).shouldCancelEvent(event);
verifyZeroInteractions(event);
withServiceMock(listenerService)
.check(listener::onEntityTarget, EntityTargetEvent.class)
.check(listener::onFoodLevelChange, FoodLevelChangeEvent.class)
.check(listener::onShoot, EntityShootBowEvent.class)
.check(listener::onEntityInteract, EntityInteractEvent.class)
.check(listener::onLowestEntityInteract, EntityInteractEvent.class);
}
@Test

View File

@ -1,14 +1,11 @@
package fr.xephi.authme.listener;
import fr.xephi.authme.ReflectionTestUtils;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityEvent;
import org.bukkit.event.player.PlayerEvent;
import java.lang.reflect.Method;
import java.util.function.Consumer;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
@ -16,11 +13,25 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
/**
* Utilities for testing AuthMe listener classes.
* Tests simple listener methods that should cancel an event when the listener service says so.
*/
public final class ListenerTestUtils {
public final class EventCancelVerifier {
private ListenerTestUtils() {
private final ListenerService listenerService;
private EventCancelVerifier(ListenerService listenerService) {
this.listenerService = listenerService;
}
/**
* Creates a new verifier that uses the given ListenerService mock (needs to be the same instance
* as used in the listener class to test).
*
* @param listenerService the listener service mock
* @return new verifier
*/
public static EventCancelVerifier withServiceMock(ListenerService listenerService) {
return new EventCancelVerifier(listenerService);
}
/**
@ -29,25 +40,23 @@ public final class ListenerTestUtils {
* canceled when the service says so and the other way around. Do not use this
* method if the handler method has additional behavior.
*
*
* @param listener the listener to test
* @param listenerService the listener service mock
* @param listenerMethod the listener method to test
* @param clazz the event class to test the handler method for
* @param <T> the event type
* @return the verifier (for chaining of methods)
*/
public static <T extends Event & Cancellable>
void checkEventIsCanceledForUnauthed(Listener listener, ListenerService listenerService, Class<T> clazz) {
Method handlerMethod = findMethod(listener, clazz);
public <T extends Event & Cancellable> EventCancelVerifier check(Consumer<T> listenerMethod, Class<T> clazz) {
T event = mock(clazz);
mockShouldCancel(true, listenerService, event);
ReflectionTestUtils.invokeMethod(handlerMethod, listener, event);
listenerMethod.accept(event);
verify(event).setCancelled(true);
event = mock(clazz);
mockShouldCancel(false, listenerService, event);
ReflectionTestUtils.invokeMethod(handlerMethod, listener, event);
listenerMethod.accept(event);
verifyZeroInteractions(event);
return this;
}
/**
@ -67,33 +76,4 @@ public final class ListenerTestUtils {
throw new IllegalStateException("Found event with unsupported type: " + event.getClass());
}
}
/**
* Returns the method in the listener that takes the given event type as parameter.
*
* @param listener the listener to scan
* @param paramType the event type
* @return the mapped method
* @throws IllegalStateException if there is not exactly one method with the given event type as parameter
*/
private static Method findMethod(Listener listener, Class<?> paramType) {
Method matchingMethod = null;
for (Method method : listener.getClass().getMethods()) {
if (method.isAnnotationPresent(EventHandler.class)) {
Class<?>[] parameters = method.getParameterTypes();
if (parameters.length == 1 && parameters[0] == paramType) {
if (matchingMethod == null) {
matchingMethod = method;
} else {
throw new IllegalStateException("Found multiple eligible methods for " + paramType);
}
}
}
}
if (matchingMethod == null) {
throw new IllegalStateException("Found no matching method for " + paramType);
}
return matchingMethod;
}
}

View File

@ -7,6 +7,8 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static fr.xephi.authme.listener.EventCancelVerifier.withServiceMock;
/**
* Test for {@link PlayerListener16}.
*/
@ -21,7 +23,8 @@ public class PlayerListener16Test {
@Test
public void shouldCancelEvent() {
ListenerTestUtils.checkEventIsCanceledForUnauthed(listener, listenerService, PlayerEditBookEvent.class);
withServiceMock(listenerService)
.check(listener::onPlayerEditBook, PlayerEditBookEvent.class);
}
}

View File

@ -7,6 +7,8 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static fr.xephi.authme.listener.EventCancelVerifier.withServiceMock;
/**
* Test for {@link PlayerListener18}.
*/
@ -21,7 +23,8 @@ public class PlayerListener18Test {
@Test
public void shouldCancelEvent() {
ListenerTestUtils.checkEventIsCanceledForUnauthed(listener, listenerService, PlayerInteractAtEntityEvent.class);
withServiceMock(listenerService)
.check(listener::onPlayerInteractAtEntity, PlayerInteractAtEntityEvent.class);
}
}

View File

@ -7,6 +7,8 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static fr.xephi.authme.listener.EventCancelVerifier.withServiceMock;
/**
* Test for {@link PlayerListener19}.
*/
@ -21,7 +23,8 @@ public class PlayerListener19Test {
@Test
public void shouldCancelEvent() {
ListenerTestUtils.checkEventIsCanceledForUnauthed(listener, listenerService, PlayerSwapHandItemsEvent.class);
withServiceMock(listenerService)
.check(listener::onPlayerSwapHandItems, PlayerSwapHandItemsEvent.class);
}
}

View File

@ -1,18 +1,18 @@
package fr.xephi.authme.listener;
import fr.xephi.authme.service.AntiBotService;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.service.AntiBotService;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.TeleportationService;
import fr.xephi.authme.service.ValidationService;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.TeleportationService;
import fr.xephi.authme.service.ValidationService;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.World;
@ -45,7 +45,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import static fr.xephi.authme.listener.ListenerTestUtils.checkEventIsCanceledForUnauthed;
import static fr.xephi.authme.listener.EventCancelVerifier.withServiceMock;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
@ -153,15 +153,16 @@ public class PlayerListenerTest {
@Test
public void shouldHandleSimpleCancelableEvents() {
checkEventIsCanceledForUnauthed(listener, listenerService, PlayerShearEntityEvent.class);
checkEventIsCanceledForUnauthed(listener, listenerService, PlayerFishEvent.class);
checkEventIsCanceledForUnauthed(listener, listenerService, PlayerBedEnterEvent.class);
checkEventIsCanceledForUnauthed(listener, listenerService, PlayerDropItemEvent.class);
checkEventIsCanceledForUnauthed(listener, listenerService, EntityDamageByEntityEvent.class);
checkEventIsCanceledForUnauthed(listener, listenerService, PlayerItemConsumeEvent.class);
checkEventIsCanceledForUnauthed(listener, listenerService, PlayerInteractEvent.class);
checkEventIsCanceledForUnauthed(listener, listenerService, PlayerPickupItemEvent.class);
checkEventIsCanceledForUnauthed(listener, listenerService, PlayerInteractEntityEvent.class);
withServiceMock(listenerService)
.check(listener::onPlayerShear, PlayerShearEntityEvent.class)
.check(listener::onPlayerFish, PlayerFishEvent.class)
.check(listener::onPlayerBedEnter, PlayerBedEnterEvent.class)
.check(listener::onPlayerDropItem, PlayerDropItemEvent.class)
.check(listener::onPlayerHitPlayerEvent, EntityDamageByEntityEvent.class)
.check(listener::onPlayerConsumeItem, PlayerItemConsumeEvent.class)
.check(listener::onPlayerInteract, PlayerInteractEvent.class)
.check(listener::onPlayerPickupItem, PlayerPickupItemEvent.class)
.check(listener::onPlayerInteractEntity, PlayerInteractEntityEvent.class);
}
@Test

View File

@ -21,6 +21,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
/**
* Test for {@link ServerListener}.
@ -56,82 +57,22 @@ public class ServerListenerTest {
@Test
public void shouldForwardPluginNameOnEnable() {
checkEnableHandling(ESSENTIALS, new Runnable() {
@Override
public void run() {
verify(pluginHookService).tryHookToEssentials();
}
});
checkEnableHandling(ESSENTIALS_SPAWN, new Runnable() {
@Override
public void run() {
verify(spawnLoader).loadEssentialsSpawn();
}
});
checkEnableHandling(MULTIVERSE, new Runnable() {
@Override
public void run() {
verify(pluginHookService).tryHookToMultiverse();
}
});
checkEnableHandling(COMBAT_TAG, new Runnable() {
@Override
public void run() {
verify(pluginHookService).tryHookToCombatPlus();
}
});
checkEnableHandling(PROTOCOL_LIB, new Runnable() {
@Override
public void run() {
verify(protocolLibService).setup();
}
});
checkEnableHandling("UnknownPlugin", new Runnable() {
@Override
public void run() {
// nothing
}
});
checkEnableHandling(ESSENTIALS, () -> verify(pluginHookService).tryHookToEssentials());
checkEnableHandling(ESSENTIALS_SPAWN, () -> verify(spawnLoader).loadEssentialsSpawn());
checkEnableHandling(MULTIVERSE, () -> verify(pluginHookService).tryHookToMultiverse());
checkEnableHandling(COMBAT_TAG, () -> verify(pluginHookService).tryHookToCombatPlus());
checkEnableHandling(PROTOCOL_LIB, () -> verify(protocolLibService).setup());
checkEnableHandling("UnknownPlugin", () -> verifyZeroInteractions(pluginHookService, spawnLoader));
}
@Test
public void shouldForwardPluginNameOnDisable() {
checkDisableHandling(ESSENTIALS, new Runnable() {
@Override
public void run() {
verify(pluginHookService).unhookEssentials();
}
});
checkDisableHandling(ESSENTIALS_SPAWN, new Runnable() {
@Override
public void run() {
verify(spawnLoader).unloadEssentialsSpawn();
}
});
checkDisableHandling(MULTIVERSE, new Runnable() {
@Override
public void run() {
verify(pluginHookService).unhookMultiverse();
}
});
checkDisableHandling(COMBAT_TAG, new Runnable() {
@Override
public void run() {
verify(pluginHookService).unhookCombatPlus();
}
});
checkDisableHandling(PROTOCOL_LIB, new Runnable() {
@Override
public void run() {
verify(protocolLibService).disable();
}
});
checkDisableHandling("UnknownPlugin", new Runnable() {
@Override
public void run() {
// nothing
}
});
checkDisableHandling(ESSENTIALS, () -> verify(pluginHookService).unhookEssentials());
checkDisableHandling(ESSENTIALS_SPAWN, () -> verify(spawnLoader).unloadEssentialsSpawn());
checkDisableHandling(MULTIVERSE, () -> verify(pluginHookService).unhookMultiverse());
checkDisableHandling(COMBAT_TAG, () -> verify(pluginHookService).unhookCombatPlus());
checkDisableHandling(PROTOCOL_LIB, () -> verify(protocolLibService).disable());
checkDisableHandling("UnknownPlugin", () -> verifyZeroInteractions(pluginHookService, spawnLoader));
}
@Test