From 86000396fddd973be080ffd5105d733f0d7f1c74 Mon Sep 17 00:00:00 2001 From: Joseph Hale Date: Fri, 2 Jul 2021 22:04:40 -0700 Subject: [PATCH] Add lots more unit tests. All arrows now have some test coverage due to the RegisteredArrowsTest that ensures the abstract methods of PowerArrow are implemented properly. Additional, detailed tests have been added for the Explosive, ForestFire, and Net Arrows. Now just to write full tests for the Sniper and Teleport Arrows! --- build.gradle | 1 + .../jhale1805/powerarrow/PowerArrow.java | 5 + .../jhale1805/powerarrow/SniperArrow.java | 2 +- .../jhale1805/powerarrow/TeleportArrow.java | 2 +- .../jhale1805/powerarrow/TorchArrow.java | 2 +- .../powerarrow/ExplosiveArrowTest.java | 88 ++++++++++++++++++ .../powerarrow/ForestFireArrowTest.java | 77 ++++++++++++++++ .../jhale1805/powerarrow/NetArrowTest.java | 77 ++++++++++++++++ .../powerarrow/RegisteredArrowsTest.java | 91 +++++++++++++++++++ .../jhale1805/powerarrow/TorchArrowTest.java | 35 ------- 10 files changed, 342 insertions(+), 38 deletions(-) create mode 100644 src/test/java/io/github/jhale1805/powerarrow/ExplosiveArrowTest.java create mode 100644 src/test/java/io/github/jhale1805/powerarrow/ForestFireArrowTest.java create mode 100644 src/test/java/io/github/jhale1805/powerarrow/NetArrowTest.java create mode 100644 src/test/java/io/github/jhale1805/powerarrow/RegisteredArrowsTest.java 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 -- 2.51.2