diff --git a/build.gradle b/build.gradle index 16d5803..6976ee4 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,7 @@ repositories { dependencies { implementation 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT' // The Spigot API with no shadowing. Requires the OSS repo. + // Unit testing packages testImplementation 'junit:junit:4.12' testImplementation 'com.github.seeseemelk:MockBukkit-v1.16:1.0.0' // MockBukkit server required for Unit Testing testImplementation "org.mockito:mockito-inline:3.+" // Used for Unit Testing, this particular artifact can mock final methods. diff --git a/src/main/java/io/github/jhale1805/powerarrow/PowerArrow.java b/src/main/java/io/github/jhale1805/powerarrow/PowerArrow.java index 12a9430..3406a6a 100644 --- a/src/main/java/io/github/jhale1805/powerarrow/PowerArrow.java +++ b/src/main/java/io/github/jhale1805/powerarrow/PowerArrow.java @@ -203,5 +203,10 @@ public abstract class PowerArrow extends ItemStack implements Listener { ItemStack other = (ItemStack) obj; return isSimilar(other) && other.getAmount() == this.getAmount(); } + + @Override + public String toString() { + return getClass().getSimpleName(); + } } diff --git a/src/main/java/io/github/jhale1805/powerarrow/SniperArrow.java b/src/main/java/io/github/jhale1805/powerarrow/SniperArrow.java index b94a3e0..14f88cb 100644 --- a/src/main/java/io/github/jhale1805/powerarrow/SniperArrow.java +++ b/src/main/java/io/github/jhale1805/powerarrow/SniperArrow.java @@ -40,7 +40,7 @@ public class SniperArrow extends PowerArrow { @Override public String[] getUsageInstructions() { - return new String[] {"Fire with Power to", "hit crosshair target."}; + return new String[] {"Directly hits target", "in crosshairs when", "shot with Power."}; } @Override diff --git a/src/main/java/io/github/jhale1805/powerarrow/TeleportArrow.java b/src/main/java/io/github/jhale1805/powerarrow/TeleportArrow.java index 70ad29c..047c704 100644 --- a/src/main/java/io/github/jhale1805/powerarrow/TeleportArrow.java +++ b/src/main/java/io/github/jhale1805/powerarrow/TeleportArrow.java @@ -26,7 +26,7 @@ public class TeleportArrow extends PowerArrow { @Override public String[] getUsageInstructions() { - return new String[] {"Teleports you to", "where it lands"}; + return new String[] {"Teleports you to", "where it lands."}; } @Override diff --git a/src/main/java/io/github/jhale1805/powerarrow/TorchArrow.java b/src/main/java/io/github/jhale1805/powerarrow/TorchArrow.java index ee412ba..b17ed85 100644 --- a/src/main/java/io/github/jhale1805/powerarrow/TorchArrow.java +++ b/src/main/java/io/github/jhale1805/powerarrow/TorchArrow.java @@ -27,7 +27,7 @@ public class TorchArrow extends PowerArrow { @Override public String[] getUsageInstructions() { - return new String[]{"Shoot with Flame to", "place a torch."}; + return new String[]{"Places a torch when", "shot with Flame."}; } @Override diff --git a/src/test/java/io/github/jhale1805/powerarrow/ExplosiveArrowTest.java b/src/test/java/io/github/jhale1805/powerarrow/ExplosiveArrowTest.java new file mode 100644 index 0000000..262f26a --- /dev/null +++ b/src/test/java/io/github/jhale1805/powerarrow/ExplosiveArrowTest.java @@ -0,0 +1,88 @@ + +package io.github.jhale1805.powerarrow; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import io.github.jhale1805.PowerProjectilePlugin; +import io.github.jhale1805.util.Utilities; + +import org.junit.*; + +import static org.mockito.Mockito.*; + +import org.mockito.MockedStatic; +import org.bukkit.Location; +import org.bukkit.entity.Projectile; +import org.bukkit.event.entity.ProjectileHitEvent; + +public class ExplosiveArrowTest { + + private static ServerMock mockServer; + + @BeforeClass + public static void setUpClass() { + mockServer = spy(new ServerMock()); // Fill in ServerMock's missing methods. + MockBukkit.mock(mockServer); + MockBukkit.load(PowerProjectilePlugin.class); + } + + @Before + public void setUp() { + + } + + @After + public void tearDown() { + + } + + @AfterClass + public static void tearDownClass() { + MockBukkit.unmock(); + } + + @Test + public void test_onThisProjectileHit_noFlame() { + // Setup mocks + ProjectileHitEvent mockEvent = mock(ProjectileHitEvent.class, RETURNS_DEEP_STUBS); + when(mockEvent.getEntity().getFireTicks()).thenReturn(0); + // Execute test + new ExplosiveArrow().onThisProjectileHit(mockEvent); + // Verify that none of the createExplostion methods were called: https://stackoverflow.com/a/46038313/14765128 + verifyNoInteractions(mockEvent.getEntity().getWorld()); + } + + @Test + public void test_onThisProjectileHit_noHitLocation() { + // Setup mocks + ProjectileHitEvent mockEvent = mock(ProjectileHitEvent.class, RETURNS_DEEP_STUBS); + when(mockEvent.getEntity().getFireTicks()).thenReturn(10); + // Execute test + try (MockedStatic mockUtils = mockStatic(Utilities.class)) { + mockUtils.when(() -> Utilities.getImpactLocation(any(ProjectileHitEvent.class))) + .thenReturn(null); + new ExplosiveArrow().onThisProjectileHit(mockEvent); + // Verify that none of the createExplostion methods were called: https://stackoverflow.com/a/46038313/14765128 + verifyNoInteractions(mockEvent.getEntity().getWorld()); + } + } + + @Test + public void test_onThisProjectileHit_hitLocation() { + // Setup mocks + ProjectileHitEvent mockEvent = mock(ProjectileHitEvent.class, RETURNS_DEEP_STUBS); + Projectile mockArrow = mockEvent.getEntity(); + Location mockLocation = mock(Location.class); + when(mockEvent.getEntity().getFireTicks()).thenReturn(10); + // Execute test + try (MockedStatic mockUtils = mockStatic(Utilities.class)) { + mockUtils.when(() -> Utilities.getImpactLocation(any(ProjectileHitEvent.class))) + .thenReturn(mockLocation); + new ExplosiveArrow().onThisProjectileHit(mockEvent); + // Verify results + verify(mockEvent.getEntity().getWorld()).createExplosion( + eq(mockLocation), anyFloat(), anyBoolean(), anyBoolean(), eq(mockArrow)); + } + } + +} \ No newline at end of file diff --git a/src/test/java/io/github/jhale1805/powerarrow/ForestFireArrowTest.java b/src/test/java/io/github/jhale1805/powerarrow/ForestFireArrowTest.java new file mode 100644 index 0000000..61f643a --- /dev/null +++ b/src/test/java/io/github/jhale1805/powerarrow/ForestFireArrowTest.java @@ -0,0 +1,77 @@ + +package io.github.jhale1805.powerarrow; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import io.github.jhale1805.PowerProjectilePlugin; +import io.github.jhale1805.util.Utilities; + +import org.junit.*; + +import static org.mockito.Mockito.*; +import org.mockito.MockedStatic; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.event.entity.ProjectileHitEvent; + +public class ForestFireArrowTest { + + private static ServerMock mockServer; + + @BeforeClass + public static void setUpClass() { + mockServer = spy(new ServerMock()); // Fill in ServerMock's missing methods. + MockBukkit.mock(mockServer); + MockBukkit.load(PowerProjectilePlugin.class); + } + + @Before + public void setUp() { + + } + + @After + public void tearDown() { + + } + + @AfterClass + public static void tearDownClass() { + MockBukkit.unmock(); + } + + @Test + public void test_onThisProjectileHit_hitLocation() { + // Setup mocks + ProjectileHitEvent mockEvent = mock(ProjectileHitEvent.class, RETURNS_DEEP_STUBS); + when(mockEvent.getHitBlock()).thenReturn(null); + // Execute test + try (MockedStatic mockUtils = mockStatic(Utilities.class)) { + mockUtils.when(() -> Utilities.getImpactLocation(any(ProjectileHitEvent.class))) + .thenReturn(mock(Location.class)); + new ForestFireArrow().onThisProjectileHit(mockEvent); + // Verify results + mockUtils.verify(() -> Utilities.replaceAirWith( + eq(Material.FIRE), any(Location.class), anyDouble())); + } + } + + @Test + public void test_onThisProjectileHit_noHitLocation() { + // Setup mocks + ProjectileHitEvent mockEvent = mock(ProjectileHitEvent.class, RETURNS_DEEP_STUBS); + when(mockEvent.getHitBlock()).thenReturn(null); + // Execute test + try (MockedStatic mockUtils = mockStatic(Utilities.class)) { + mockUtils.when(() -> Utilities.getImpactLocation(any(ProjectileHitEvent.class))) + .thenReturn(null); + new ForestFireArrow().onThisProjectileHit(mockEvent); + // Verify results + mockUtils.verify(() -> Utilities.replaceAirWith( + any(Material.class), any(Location.class), anyDouble()), + never()); + } + } + +} \ No newline at end of file diff --git a/src/test/java/io/github/jhale1805/powerarrow/NetArrowTest.java b/src/test/java/io/github/jhale1805/powerarrow/NetArrowTest.java new file mode 100644 index 0000000..49af0cc --- /dev/null +++ b/src/test/java/io/github/jhale1805/powerarrow/NetArrowTest.java @@ -0,0 +1,77 @@ + +package io.github.jhale1805.powerarrow; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import io.github.jhale1805.PowerProjectilePlugin; +import io.github.jhale1805.util.Utilities; + +import org.junit.*; +import org.mockito.MockedStatic; + +import static org.mockito.Mockito.*; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.event.entity.ProjectileHitEvent; + +public class NetArrowTest { + + private static ServerMock mockServer; + + @BeforeClass + public static void setUpClass() { + mockServer = spy(new ServerMock()); // Fill in ServerMock's missing methods. + MockBukkit.mock(mockServer); + MockBukkit.load(PowerProjectilePlugin.class); + } + + @Before + public void setUp() { + + } + + @After + public void tearDown() { + + } + + @AfterClass + public static void tearDownClass() { + MockBukkit.unmock(); + } + + @Test + public void test_onThisProjectileHit_hitLocation() { + // Setup mocks + ProjectileHitEvent mockEvent = mock(ProjectileHitEvent.class, RETURNS_DEEP_STUBS); + when(mockEvent.getHitBlock()).thenReturn(null); + // Execute test + try (MockedStatic mockUtils = mockStatic(Utilities.class)) { + mockUtils.when(() -> Utilities.getImpactLocation(any(ProjectileHitEvent.class))) + .thenReturn(mock(Location.class)); + new NetArrow().onThisProjectileHit(mockEvent); + // Verify results + mockUtils.verify(() -> Utilities.replaceAirWith( + eq(Material.COBWEB), any(Location.class), anyDouble())); + } + } + + @Test + public void test_onThisProjectileHit_noHitLocation() { + // Setup mocks + ProjectileHitEvent mockEvent = mock(ProjectileHitEvent.class, RETURNS_DEEP_STUBS); + when(mockEvent.getHitBlock()).thenReturn(null); + // Execute test + try (MockedStatic mockUtils = mockStatic(Utilities.class)) { + mockUtils.when(() -> Utilities.getImpactLocation(any(ProjectileHitEvent.class))) + .thenReturn(null); + new NetArrow().onThisProjectileHit(mockEvent); + // Verify results + mockUtils.verify(() -> Utilities.replaceAirWith( + any(Material.class), any(Location.class), anyDouble()), + never()); + } + } + +} \ No newline at end of file diff --git a/src/test/java/io/github/jhale1805/powerarrow/RegisteredArrowsTest.java b/src/test/java/io/github/jhale1805/powerarrow/RegisteredArrowsTest.java new file mode 100644 index 0000000..70126fd --- /dev/null +++ b/src/test/java/io/github/jhale1805/powerarrow/RegisteredArrowsTest.java @@ -0,0 +1,91 @@ +package io.github.jhale1805.powerarrow; + +import be.seeseemelk.mockbukkit.MockBukkit; +import be.seeseemelk.mockbukkit.ServerMock; +import io.github.jhale1805.PowerProjectilePlugin; + +import org.junit.*; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import static org.junit.Assert.*; + +import static org.mockito.Mockito.*; + +import org.bukkit.inventory.Recipe; + +/** + * Tests the implementations of PowerArrow's abstract methods of all + * registered arrows to make sure they follow the specifications in + * the javadocs. + */ +@RunWith(Parameterized.class) +public class RegisteredArrowsTest { + + private static ServerMock mockServer; + + static { + mockServer = spy(new ServerMock()); // Fill in ServerMock's missing methods. + MockBukkit.mock(mockServer); + MockBukkit.load(PowerProjectilePlugin.class); + } + + @Parameters(name="{0}") + public static Object[] data() { + return PowerArrowRegistry.getPowerArrows(); + } + + @Parameter + public PowerArrow arrowUnderTest; + + @BeforeClass + public static void setUpClass() { + + } + + @Before + public void setUp() { + + } + + @After + public void tearDown() { + + } + + @AfterClass + public static void tearDownClass() { + MockBukkit.unmock(); + } + + @Test + public void test_getName() { + String name = arrowUnderTest.getName(); + assertTrue("PowerArrow names must be snake_case (no spaces).", + !name.contains(" ")); + assertTrue("PowerArrow names must be snake_case (no capital letters).", + name.equals(name.toLowerCase())); + } + + @Test + public void test_getUsageInstructions() { + String[] instructions = arrowUnderTest.getUsageInstructions(); + assertTrue("Instructions cannot be > 3 lines.", + instructions.length <= 3); + for (String line : instructions) { + assertTrue("Instruction lines cannot be > 20 characters.", + line.length() <= 20); + } + } + + @Test + public void test_getRecipe() { + Recipe recipe = arrowUnderTest.getRecipe(); + assertNotNull("A PowerArrow's recipe cannot be `null`.", recipe); + assertTrue("A PowerArrow's recipe cannot yield alternate items.", + recipe.getResult().isSimilar(arrowUnderTest)); + } + +} \ No newline at end of file diff --git a/src/test/java/io/github/jhale1805/powerarrow/TorchArrowTest.java b/src/test/java/io/github/jhale1805/powerarrow/TorchArrowTest.java index e6bdb05..659d5c5 100644 --- a/src/test/java/io/github/jhale1805/powerarrow/TorchArrowTest.java +++ b/src/test/java/io/github/jhale1805/powerarrow/TorchArrowTest.java @@ -6,7 +6,6 @@ import be.seeseemelk.mockbukkit.ServerMock; import io.github.jhale1805.PowerProjectilePlugin; import org.junit.*; -import static org.junit.Assert.*; import static org.mockito.Mockito.*; import org.bukkit.Location; @@ -15,7 +14,6 @@ import org.bukkit.block.BlockFace; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Directional; import org.bukkit.event.entity.ProjectileHitEvent; -import org.bukkit.inventory.Recipe; public class TorchArrowTest { @@ -28,44 +26,11 @@ public class TorchArrowTest { MockBukkit.load(PowerProjectilePlugin.class); } - @Before - public void setUp() { - - } - - @After - public void tearDown() { - - } - @AfterClass public static void tearDownClass() { MockBukkit.unmock(); } - @Test - public void test_getName() { - String name = new TorchArrow().getName(); - assertTrue(!name.contains(" ")); - assertTrue(name.equals(name.toLowerCase())); - } - - @Test - public void test_getUsageInstructions() { - String[] instructions = new TorchArrow().getUsageInstructions(); - assertTrue(instructions.length <= 3); - for (String line : instructions) { - assertTrue(line.length() <= 20); - } - } - - @Test - public void test_getRecipe() { - Recipe recipe = new TorchArrow().getRecipe(); - assertNotNull(recipe); - assertTrue(recipe.getResult().isSimilar(new TorchArrow())); - } - @Test public void test_onThisProjectileHit_noFlame() { // Setup mocks