Skip to content

Commit

Permalink
Allow developers to load custom MockCommandAPIBukkit instances in the…
Browse files Browse the repository at this point in the history
…ir tests

This makes it possible to avoid UnimplementedMethodExceptions by overriding and implementing the method yourself

NOTE: CommandAPIVersionHandler was changed from an interface to an abstract class so the test-toolkit version could have a non-final field. I don't think this affects anything else.
  • Loading branch information
willkroboth committed Aug 24, 2024
1 parent 525f12d commit f71519e
Show file tree
Hide file tree
Showing 13 changed files with 143 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* file within the commandapi-core module is NOT used at run time. Instead, the platform modules
* replace this class with their own version that handles loads the correct class for their version.
*/
public interface CommandAPIVersionHandler {
public abstract class CommandAPIVersionHandler {

/**
* Returns an instance of the version's implementation of CommandAPIPlatform.
Expand Down
26 changes: 14 additions & 12 deletions commandapi-documentation-code/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,25 +56,15 @@
<scope>provided</scope>
</dependency>

<!-- CommandAPI -->
<dependency>
<groupId>dev.jorel</groupId>
<artifactId>commandapi-bukkit-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.jorel</groupId>
<artifactId>commandapi-bukkit-kotlin</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Testing examples dependencies -->
<dependency>
<groupId>com.github.seeseemelk</groupId>
<artifactId>MockBukkit-v1.20</artifactId>
<version>3.9.0</version>
</dependency>
<dependency>
<!-- Note: This is before the other CommandAPI dependencies so example `testLoadMockCommandAPI2`
can resolve the correct instance of the CommandAPIVersionHandler class -->
<groupId>dev.jorel</groupId>
<artifactId>commandapi-bukkit-test-toolkit</artifactId>
<version>${project.version}</version>
Expand All @@ -85,6 +75,18 @@
<version>5.8.2</version>
</dependency>

<!-- CommandAPI -->
<dependency>
<groupId>dev.jorel</groupId>
<artifactId>commandapi-bukkit-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.jorel</groupId>
<artifactId>commandapi-bukkit-kotlin</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Other -->
<dependency>
<groupId>de.tr7zw</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2527,7 +2527,9 @@ class test {
class Main extends JavaPlugin {

}
/* ANCHOR: testLoadMockPlugin1 */

class example1 {
/* ANCHOR: testLoadMockCommandAPI1 */
@BeforeEach
public void setUp() {
// Set up MockBukkit server
Expand All @@ -2547,7 +2549,32 @@ public void tearDown() {
// Reset for a clean slate next test
MockBukkit.unmock();
}
/* ANCHOR_END: testLoadMockPlugin1 */
/* ANCHOR_END: testLoadMockCommandAPI1 */
}

class example2 {
/* ANCHOR: testLoadMockCommandAPI2 */
public class CustomMockCommandAPIBukkit extends MockCommandAPIBukkit {
// Implement a method that usually throws `UnimplementedMethodException`
@Override
public void reloadDataPacks() {
CommandAPI.logInfo("Simulating data pack reload");
// Further logic
}
}

@BeforeEach
public void setUp() {
// Set up MockBukkit server
MockBukkit.mock();

// Tell the CommandAPI to use your custom platform implementation
CommandAPIVersionHandler.usePlatformImplementation(new CustomMockCommandAPIBukkit());

// Load CommandAPI and your plugin as mentioned above...
}
/* ANCHOR_END: testLoadMockCommandAPI2 */
}
}

class tooltips {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
package dev.jorel.commandapi;

public interface CommandAPIVersionHandler {
static CommandAPIPlatform<?, ?, ?> getPlatform() {
return new MockCommandAPIBukkit();
public abstract class CommandAPIVersionHandler {
// Allow loading a different platform implementation (most likely to implement something `MockCommandAPIBukkit` doesn't)
private static CommandAPIPlatform<?, ?, ?> alternativePlatform = null;

/**
* Configures the test kit to use the given {@link CommandAPIPlatform} when the CommandAPI is loaded.
*
* @param platform The {@link CommandAPIPlatform} to use for the next test. This will likely be a custom
* implementation of {@link MockCommandAPIBukkit} that overrides a method you need to run
* tests that doesn't have a proper implementation in {@link MockCommandAPIBukkit}.
*/
public static void usePlatformImplementation(CommandAPIPlatform<?, ?, ?> platform) {
alternativePlatform = platform;
}

static LoadContext getPlatform() {
// Default to MockCommandAPIBukkit if not given
CommandAPIPlatform<?, ?, ?> platform = alternativePlatform == null ? new MockCommandAPIBukkit() : alternativePlatform;

// Reset to avoid platform persisting between tests
alternativePlatform = null;

return new LoadContext(platform);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public MockCommandSource getBrigadierSourceFromCommandSender(AbstractCommandSend
* using {@link CommandAPI#setLogger(CommandAPILogger)} and set this to {@code true} before calling
* {@link CommandAPI#onLoad(CommandAPIConfig)}, then the CommandAPI will write messages into the test log.
*/
public boolean ENABLE_LOGGING = false;
public static boolean ENABLE_LOGGING = false;

@Override
public CommandAPILogger getLogger() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package dev.jorel.commandapi;

import be.seeseemelk.mockbukkit.MockBukkit;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
* Tests for using {@link CommandAPIVersionHandler#usePlatformImplementation(CommandAPIPlatform)}
*/
class CommandAPIVersionHandlerOverridingTests {
// Setup
@BeforeEach
public void setUp() {
MockBukkit.mock();
}

@AfterEach
public void tearDown() {
MockBukkit.unmock();
}

private static class CustomMockCommandAPIBukkit extends MockCommandAPIBukkit {

}

// Tests
@Test
void testDefaultPlatform() {
MockCommandAPIPlugin.load();

assertEquals(MockCommandAPIBukkit.class, CommandAPITestUtilities.getCommandAPIPlatform().getClass());
}

@Test
void testChangingPlatform() {
CommandAPIVersionHandler.usePlatformImplementation(new CustomMockCommandAPIBukkit());
MockCommandAPIPlugin.load();

assertEquals(CustomMockCommandAPIBukkit.class, CommandAPITestUtilities.getCommandAPIPlatform().getClass());
}

@Test
void testPlatformDoesNotPersist() {
CommandAPIVersionHandler.usePlatformImplementation(new CustomMockCommandAPIBukkit());
MockCommandAPIPlugin.load();

assertEquals(CustomMockCommandAPIBukkit.class, CommandAPITestUtilities.getCommandAPIPlatform().getClass());

MockBukkit.unmock();
MockBukkit.mock();

MockCommandAPIPlugin.load();
assertEquals(MockCommandAPIBukkit.class, CommandAPITestUtilities.getCommandAPIPlatform().getClass());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import dev.jorel.commandapi.test.MockNMS;
* file within the commandapi-core module is NOT used at run time. Instead, the platform modules
* replace this class with their own version that handles loads the correct class for their version
*/
interface CommandAPIVersionHandler {
abstract class CommandAPIVersionHandler {

companion object {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* file within the commandapi-core module is NOT used at run time. Instead, the platform modules
* replace this class with their own version that handles loads the correct class for their version
*/
public interface CommandAPIVersionHandler {
public abstract class CommandAPIVersionHandler {

public static final String profileId = getProfileId();
public static final boolean IS_MOJANG_MAPPED = isMojangMapped();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
* depends on CommandListenerWrapper and then our main NMS_VERSION class extends
* that.
*/
public interface CommandAPIVersionHandler {
public abstract class CommandAPIVersionHandler {

/**
* Returns an instance of the current running version's implementation of the Bukkit NMS.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package dev.jorel.commandapi;

public interface CommandAPIVersionHandler {
public abstract class CommandAPIVersionHandler {
static CommandAPIPlatform<?, ?, ?> getPlatform() {
return new CommandAPISponge();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package dev.jorel.commandapi;

public interface CommandAPIVersionHandler {
public abstract class CommandAPIVersionHandler {
static LoadContext getPlatform() {
return new LoadContext(new CommandAPIVelocity());
}
Expand Down
2 changes: 1 addition & 1 deletion docssrc/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@

- [Testing Commands](./test_intro.md)
- [Set Up](./test_setup.md)
- [Loading Test CommandAPI](./test_loadmockplugin.md)
- [Loading Test CommandAPI](./test_loadmockcommandapi.md)
- [Testing Utilities](./test_utilities.md)

-----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,21 @@ Loads the CommandAPI Plugin after applying the given consumer. This allows confi
To change, for example, the `missing-executor-implementation` message while running tests, you can use the method `CommandAPIBukkitConfig#missingExecutorImplementationMessage` when the `configureSettings` callback is run:

```java
{{#include ../../commandapi-documentation-code/src/main/java/dev/jorel/commandapi/examples/java/Examples.java:testLoadMockPlugin1}}
{{#include ../../commandapi-documentation-code/src/main/java/dev/jorel/commandapi/examples/java/Examples.java:testLoadMockCommandAPI1}}
```

</div>

## Shaded Dependency

If your plugin shades the CommandAPI, the CommandAPI will automatically load as usual when you use MockBukkit to load your plugin. Just note that you **must** call `CommandAPI.onDisable()` in your plugin's `onDisable` method in order for the test environment to reset properly after each test.

## Loading a custom CommandAPI platform implementation

By default, the testing environment will load `MockCommandAPIBukkit` as the CommandAPI platform object. This works for basic tests, but many methods in `MockCommandAPIBukkit` are not yet implemented and just throw an `UnimplementedMethodException`. This may cause your tests to fail if your code relies on any of these methods. If you see an `UnimplementedMethodException`, please tell us about it with a [GitHub Issue](https://github.com/JorelAli/CommandAPI/issues) or a message in the CommandAPI Discord so we can get it solved for everyone.

In the short term, you can also try to avoid an `UnimplementedMethodException` by implementing the required method yourself. Simply create a class that extends `MockCommandAPIBukkit` and override the required method with an appropriate implementation. Before each test where you want to use your custom implementation, make sure to call `CommandAPIVersionHandler#usePlatformImplementation` to let the CommandAPI know what it should load.

```java
{{#include ../../commandapi-documentation-code/src/main/java/dev/jorel/commandapi/examples/java/Examples.java:testLoadMockCommandAPI2}}
```

0 comments on commit f71519e

Please sign in to comment.