Add methods to Reflection utils; test ChangePasswordCommand fully

This commit is contained in:
ljacqu 2015-11-28 20:40:02 +01:00
parent c02bf7db76
commit a1a14aa760
6 changed files with 106 additions and 46 deletions

View File

@ -66,7 +66,7 @@ public class ChangePasswordCommand extends ExecutableCommand {
// Set the password // Set the password
final AuthMe plugin = wrapper.getAuthMe(); final AuthMe plugin = wrapper.getAuthMe();
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, wrapper.getServer().getScheduler().runTaskAsynchronously(plugin,
new ChangePasswordTask(plugin, player, playerPass, playerPassVerify)); new ChangePasswordTask(plugin, player, playerPass, playerPassVerify));
return true; return true;
} }

View File

@ -36,11 +36,6 @@ public class ChangePasswordTask implements Runnable {
this.newPassword = newPassword; this.newPassword = newPassword;
} }
/**
* Method run.
*
* @see java.lang.Runnable#run()
*/
@Override @Override
public void run() { public void run() {
Messages m = plugin.getMessages(); Messages m = plugin.getMessages();

View File

@ -1,6 +1,9 @@
package fr.xephi.authme; package fr.xephi.authme;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method;
import static java.lang.String.format;
/** /**
* Offers reflection functionality to set up tests. Use only when absolutely necessary. * Offers reflection functionality to set up tests. Use only when absolutely necessary.
@ -21,13 +24,53 @@ public final class ReflectionTestUtils {
*/ */
public static <T> void setField(Class<T> clazz, T instance, String fieldName, Object value) { public static <T> void setField(Class<T> clazz, T instance, String fieldName, Object value) {
try { try {
Field field = clazz.getDeclaredField(fieldName); Field field = getField(clazz, instance, fieldName);
field.setAccessible(true);
field.set(instance, value); field.set(instance, value);
} catch (NoSuchFieldException | IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new RuntimeException( throw new RuntimeException(
String.format("Could not set value to field '%s' for instance '%s' of class '%s'", format("Could not set value to field '%s' for instance '%s' of class '%s'",
fieldName, instance, clazz.getName()), e); fieldName, instance, clazz.getName()), e);
} }
} }
private static <T> Field getField(Class<T> clazz, T instance, String fieldName) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
throw new RuntimeException(format("Could not get field '%s' for instance '%s' of class '%s'",
fieldName, instance, clazz.getName()), e);
}
}
public static <T> Object getFieldValue(Class<T> clazz, T instance, String fieldName) {
Field field = getField(clazz, instance, fieldName);
try {
return field.get(instance);
} catch (IllegalAccessException e) {
throw new RuntimeException("Could not get value of field '" + fieldName + "'");
}
}
/**
* Return the method on the given class with the supplied parameter types.
*
* @param clazz The class to retrieve a method from
* @param methodName The name of the method
* @param parameterTypes The parameter types the method to retrieve has
*
* @return The method of the class, set to be accessible
*/
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
try {
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
return method;
} catch (NoSuchMethodException e) {
throw new RuntimeException("Could not retrieve method '" + methodName + "' from class '"
+ clazz.getName() + "'");
}
}
} }

View File

@ -1,19 +1,29 @@
package fr.xephi.authme.command.executable.changepassword; package fr.xephi.authme.command.executable.changepassword;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ReflectionTestUtils;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.command.CommandParts; import fr.xephi.authme.command.CommandParts;
import fr.xephi.authme.settings.MessageKey; import fr.xephi.authme.settings.MessageKey;
import fr.xephi.authme.settings.Messages; import fr.xephi.authme.settings.Messages;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.task.ChangePasswordTask;
import fr.xephi.authme.util.WrapperMock; import fr.xephi.authme.util.WrapperMock;
import org.bukkit.Server;
import org.bukkit.command.BlockCommandSender; import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitScheduler;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentCaptor;
import java.util.Arrays; import java.util.Collections;
import static java.util.Arrays.asList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@ -22,20 +32,21 @@ import static org.mockito.Mockito.*;
*/ */
public class ChangePasswordCommandTest { public class ChangePasswordCommandTest {
private WrapperMock wrapperMock;
private Messages messagesMock; private Messages messagesMock;
private PlayerCache cacheMock; private PlayerCache cacheMock;
@Before @Before
public void setUpMocks() { public void setUpMocks() {
WrapperMock wrapper = WrapperMock.createInstance(); wrapperMock = WrapperMock.createInstance();
messagesMock = wrapper.getMessages(); messagesMock = wrapperMock.getMessages();
cacheMock = wrapper.getPlayerCache(); cacheMock = wrapperMock.getPlayerCache();
// Only allow passwords with alphanumerical characters for the test // Only allow passwords with alphanumerical characters for the test
Settings.getPassRegex = "[a-zA-Z0-9]+"; Settings.getPassRegex = "[a-zA-Z0-9]+";
Settings.getPasswordMinLen = 2; Settings.getPasswordMinLen = 2;
Settings.passwordMaxLength = 50; Settings.passwordMaxLength = 50;
// TODO ljacqu 20151126: Verify the calls to getServer() (see commented code) Settings.unsafePasswords = Collections.EMPTY_LIST;
} }
@Test @Test
@ -50,7 +61,7 @@ public class ChangePasswordCommandTest {
// then // then
verify(arguments, never()).get(anyInt()); verify(arguments, never()).get(anyInt());
//verify(pluginMock, never()).getServer(); assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false));
} }
@Test @Test
@ -64,7 +75,7 @@ public class ChangePasswordCommandTest {
// then // then
verify(messagesMock).send(sender, MessageKey.NOT_LOGGED_IN); verify(messagesMock).send(sender, MessageKey.NOT_LOGGED_IN);
//verify(pluginMock, never()).getServer(); assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false));
} }
@Test @Test
@ -78,7 +89,7 @@ public class ChangePasswordCommandTest {
// then // then
verify(messagesMock).send(sender, MessageKey.PASSWORD_MATCH_ERROR); verify(messagesMock).send(sender, MessageKey.PASSWORD_MATCH_ERROR);
//verify(pluginMock, never()).getServer(); assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false));
} }
@ -93,7 +104,7 @@ public class ChangePasswordCommandTest {
// then // then
verify(messagesMock).send(sender, MessageKey.PASSWORD_IS_USERNAME_ERROR); verify(messagesMock).send(sender, MessageKey.PASSWORD_IS_USERNAME_ERROR);
//verify(pluginMock, never()).getServer(); assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false));
} }
@Test @Test
@ -108,7 +119,7 @@ public class ChangePasswordCommandTest {
// then // then
verify(messagesMock).send(sender, MessageKey.INVALID_PASSWORD_LENGTH); verify(messagesMock).send(sender, MessageKey.INVALID_PASSWORD_LENGTH);
//verify(pluginMock, never()).getServer(); assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false));
} }
@Test @Test
@ -123,7 +134,7 @@ public class ChangePasswordCommandTest {
// then // then
verify(messagesMock).send(sender, MessageKey.INVALID_PASSWORD_LENGTH); verify(messagesMock).send(sender, MessageKey.INVALID_PASSWORD_LENGTH);
//verify(pluginMock, never()).getServer(); assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false));
} }
@Test @Test
@ -131,14 +142,34 @@ public class ChangePasswordCommandTest {
// given // given
CommandSender sender = initPlayerWithName("player", true); CommandSender sender = initPlayerWithName("player", true);
ChangePasswordCommand command = new ChangePasswordCommand(); ChangePasswordCommand command = new ChangePasswordCommand();
Settings.unsafePasswords = Arrays.asList("test", "abc123"); Settings.unsafePasswords = asList("test", "abc123");
// when // when
command.executeCommand(sender, new CommandParts(), new CommandParts("abc123")); command.executeCommand(sender, new CommandParts(), new CommandParts("abc123"));
// then // then
verify(messagesMock).send(sender, MessageKey.PASSWORD_UNSAFE_ERROR); verify(messagesMock).send(sender, MessageKey.PASSWORD_UNSAFE_ERROR);
//verify(pluginMock, never()).getServer(); assertThat(wrapperMock.wasMockCalled(Server.class), equalTo(false));
}
@Test
public void shouldForwardTheDataForValidPassword() {
// given
CommandSender sender = initPlayerWithName("parker", true);
ChangePasswordCommand command = new ChangePasswordCommand();
BukkitScheduler schedulerMock = mock(BukkitScheduler.class);
given(wrapperMock.getServer().getScheduler()).willReturn(schedulerMock);
// when
command.executeCommand(sender, new CommandParts(), new CommandParts(asList("abc123", "abc123")));
// then
verify(messagesMock, never()).send(eq(sender), any(MessageKey.class));
ArgumentCaptor<ChangePasswordTask> taskCaptor = ArgumentCaptor.forClass(ChangePasswordTask.class);
verify(schedulerMock).runTaskAsynchronously(any(AuthMe.class), taskCaptor.capture());
ChangePasswordTask task = taskCaptor.getValue();
assertThat((String) ReflectionTestUtils.getFieldValue(ChangePasswordTask.class, task, "newPassword"),
equalTo("abc123"));
} }
private Player initPlayerWithName(String name, boolean loggedIn) { private Player initPlayerWithName(String name, boolean loggedIn) {

View File

@ -23,7 +23,6 @@ import static org.mockito.Mockito.*;
*/ */
public class UtilsTest { public class UtilsTest {
private static WrapperMock wrapperMock;
private static AuthMe authMeMock; private static AuthMe authMeMock;
private PermissionsManager permissionsManagerMock; private PermissionsManager permissionsManagerMock;
@ -34,7 +33,7 @@ public class UtilsTest {
*/ */
@BeforeClass @BeforeClass
public static void setUpMocks() { public static void setUpMocks() {
wrapperMock = WrapperMock.createInstance(); WrapperMock wrapperMock = WrapperMock.createInstance();
authMeMock = wrapperMock.getAuthMe(); authMeMock = wrapperMock.getAuthMe();
} }
@ -109,16 +108,11 @@ public class UtilsTest {
} }
@Test @Test
// Note ljacqu 20151122: This is a heavy test setup with reflections... If it causes trouble, skip it with @Ignore
public void shouldRetrieveListOfOnlinePlayersFromReflectedMethod() { public void shouldRetrieveListOfOnlinePlayersFromReflectedMethod() {
// given // given
ReflectionTestUtils.setField(Utils.class, null, "getOnlinePlayersIsCollection", false); ReflectionTestUtils.setField(Utils.class, null, "getOnlinePlayersIsCollection", false);
try { ReflectionTestUtils.setField(Utils.class, null, "getOnlinePlayers",
ReflectionTestUtils.setField(Utils.class, null, "getOnlinePlayers", ReflectionTestUtils.getMethod(UtilsTest.class, "onlinePlayersImpl"));
UtilsTest.class.getDeclaredMethod("onlinePlayersImpl"));
} catch (NoSuchMethodException e) {
throw new RuntimeException("Could not get method onlinePlayersImpl() in test class", e);
}
// when // when
Collection<? extends Player> players = Utils.getOnlinePlayers(); Collection<? extends Player> players = Utils.getOnlinePlayers();

View File

@ -20,8 +20,6 @@ import java.util.logging.Logger;
public class WrapperMock extends Wrapper { public class WrapperMock extends Wrapper {
private Map<Class<?>, Object> mocks = new HashMap<>(); private Map<Class<?>, Object> mocks = new HashMap<>();
private static WrapperMock singleton;
private File getDataFolderValue = new File("/");
private WrapperMock() { private WrapperMock() {
super(); super();
@ -33,9 +31,9 @@ public class WrapperMock extends Wrapper {
* @return The created singleton * @return The created singleton
*/ */
public static WrapperMock createInstance() { public static WrapperMock createInstance() {
singleton = new WrapperMock(); WrapperMock instance = new WrapperMock();
Wrapper.setSingleton(singleton); Wrapper.setSingleton(instance);
return singleton; return instance;
} }
@Override @Override
@ -70,20 +68,19 @@ public class WrapperMock extends Wrapper {
@Override @Override
public File getDataFolder() { public File getDataFolder() {
if (singleton.getDataFolderValue != null) { return new File("/");
return singleton.getDataFolderValue;
}
return getMock(File.class);
} }
/** /**
* Set the data folder to be returned for test contexts. Defaults to File("/"); supply null to make WrapperMock * Return whether a mock of the given class type was created, i.e. verify whether a certain method was executed on
* return a mock of the File class as with the other fields. * the Wrapper to retrieve an entity.
* *
* @param file The data folder location to return * @param mockClass The class of the mock to verify
*
* @return True if the mock has been created, false otherwise
*/ */
public void setDataFolder(File file) { public boolean wasMockCalled(Class<?> mockClass) {
this.getDataFolderValue = file; return mocks.get(mockClass) != null;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")