From 1fa617d08dd5c7d88fe0264595acd8277fec47a7 Mon Sep 17 00:00:00 2001 From: Jeroen Meulemeester Date: Sun, 6 Dec 2015 22:58:16 +0100 Subject: [PATCH 1/4] Add proper unit tests for chain pattern #293 --- .../java/com/iluwatar/chain/OrcCommander.java | 1 + .../java/com/iluwatar/chain/OrcOfficer.java | 1 + .../java/com/iluwatar/chain/OrcSoldier.java | 1 + .../main/java/com/iluwatar/chain/Request.java | 66 +++++++++++++++---- .../java/com/iluwatar/chain/OrcKingTest.java | 37 +++++++++++ 5 files changed, 93 insertions(+), 13 deletions(-) create mode 100644 chain/src/test/java/com/iluwatar/chain/OrcKingTest.java diff --git a/chain/src/main/java/com/iluwatar/chain/OrcCommander.java b/chain/src/main/java/com/iluwatar/chain/OrcCommander.java index 73de1b8b2..6cc495b99 100644 --- a/chain/src/main/java/com/iluwatar/chain/OrcCommander.java +++ b/chain/src/main/java/com/iluwatar/chain/OrcCommander.java @@ -15,6 +15,7 @@ public class OrcCommander extends RequestHandler { public void handleRequest(Request req) { if (req.getRequestType().equals(RequestType.DEFEND_CASTLE)) { printHandling(req); + req.markHandled(); } else { super.handleRequest(req); } diff --git a/chain/src/main/java/com/iluwatar/chain/OrcOfficer.java b/chain/src/main/java/com/iluwatar/chain/OrcOfficer.java index 68df3ec6f..e6d68c19c 100644 --- a/chain/src/main/java/com/iluwatar/chain/OrcOfficer.java +++ b/chain/src/main/java/com/iluwatar/chain/OrcOfficer.java @@ -15,6 +15,7 @@ public class OrcOfficer extends RequestHandler { public void handleRequest(Request req) { if (req.getRequestType().equals(RequestType.TORTURE_PRISONER)) { printHandling(req); + req.markHandled(); } else { super.handleRequest(req); } diff --git a/chain/src/main/java/com/iluwatar/chain/OrcSoldier.java b/chain/src/main/java/com/iluwatar/chain/OrcSoldier.java index d96f51702..bd2a8d11d 100644 --- a/chain/src/main/java/com/iluwatar/chain/OrcSoldier.java +++ b/chain/src/main/java/com/iluwatar/chain/OrcSoldier.java @@ -15,6 +15,7 @@ public class OrcSoldier extends RequestHandler { public void handleRequest(Request req) { if (req.getRequestType().equals(RequestType.COLLECT_TAX)) { printHandling(req); + req.markHandled(); } else { super.handleRequest(req); } diff --git a/chain/src/main/java/com/iluwatar/chain/Request.java b/chain/src/main/java/com/iluwatar/chain/Request.java index 0c62cfd43..5c3256a55 100644 --- a/chain/src/main/java/com/iluwatar/chain/Request.java +++ b/chain/src/main/java/com/iluwatar/chain/Request.java @@ -1,38 +1,78 @@ package com.iluwatar.chain; +import java.util.Objects; + /** - * * Request - * */ public class Request { - private String requestDescription; - private RequestType requestType; + /** + * The type of this request, used by each item in the chain to see if they should or can handle + * this particular request + */ + private final RequestType requestType; - public Request(RequestType requestType, String requestDescription) { - this.setRequestType(requestType); - this.setRequestDescription(requestDescription); + /** + * A description of the request + */ + private final String requestDescription; + + /** + * Indicates if the request is handled or not. A request can only switch state from unhandled to + * handled, there's no way to 'unhandle' a request + */ + private boolean handled = false; + + /** + * Create a new request of the given type and accompanied description. + * + * @param requestType The type of request + * @param requestDescription The description of the request + */ + public Request(final RequestType requestType, final String requestDescription) { + this.requestType = Objects.requireNonNull(requestType); + this.requestDescription = Objects.requireNonNull(requestDescription); } + /** + * Get a description of the request + * + * @return A human readable description of the request + */ public String getRequestDescription() { return requestDescription; } - public void setRequestDescription(String requestDescription) { - this.requestDescription = requestDescription; - } - + /** + * Get the type of this request, used by each person in the chain of command to see if they should + * or can handle this particular request + * + * @return The request type + */ public RequestType getRequestType() { return requestType; } - public void setRequestType(RequestType requestType) { - this.requestType = requestType; + /** + * Mark the request as handled + */ + public void markHandled() { + this.handled = true; + } + + /** + * Indicates if this request is handled or not + * + * @return true when the request is handled, false if not + */ + public boolean isHandled() { + return this.handled; } @Override public String toString() { return getRequestDescription(); } + } diff --git a/chain/src/test/java/com/iluwatar/chain/OrcKingTest.java b/chain/src/test/java/com/iluwatar/chain/OrcKingTest.java new file mode 100644 index 000000000..fd3d573b6 --- /dev/null +++ b/chain/src/test/java/com/iluwatar/chain/OrcKingTest.java @@ -0,0 +1,37 @@ +package com.iluwatar.chain; + +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/6/15 - 9:29 PM + * + * @author Jeroen Meulemeester + */ +public class OrcKingTest { + + /** + * All possible requests + */ + private static final Request[] REQUESTS = new Request[]{ + new Request(RequestType.DEFEND_CASTLE, "Don't let the barbarians enter my castle!!"), + new Request(RequestType.TORTURE_PRISONER, "Don't just stand there, tickle him!"), + new Request(RequestType.COLLECT_TAX, "Don't steal, the King hates competition ..."), + }; + + @Test + public void testMakeRequest() throws Exception { + final OrcKing king = new OrcKing(); + + for (final Request request : REQUESTS) { + king.makeRequest(request); + assertTrue( + "Expected all requests from King to be handled, but [" + request + "] was not!", + request.isHandled() + ); + } + + } + +} \ No newline at end of file From 875e5b872c6f31b6ca616e621446b05cfd0ba7c9 Mon Sep 17 00:00:00 2001 From: Jeroen Meulemeester Date: Sun, 6 Dec 2015 23:14:07 +0100 Subject: [PATCH 2/4] Add proper unit tests for builder pattern #293 --- .../java/com/iluwatar/builder/HeroTest.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 builder/src/test/java/com/iluwatar/builder/HeroTest.java diff --git a/builder/src/test/java/com/iluwatar/builder/HeroTest.java b/builder/src/test/java/com/iluwatar/builder/HeroTest.java new file mode 100644 index 000000000..2bedf3ef1 --- /dev/null +++ b/builder/src/test/java/com/iluwatar/builder/HeroTest.java @@ -0,0 +1,56 @@ +package com.iluwatar.builder; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/6/15 - 11:01 PM + * + * @author Jeroen Meulemeester + */ +public class HeroTest { + + /** + * Test if we get the expected exception when trying to create a hero without a profession + */ + @Test(expected = IllegalArgumentException.class) + public void testMissingProfession() throws Exception { + new Hero.HeroBuilder(null, "Sir without a job"); + } + + /** + * Test if we get the expected exception when trying to create a hero without a name + */ + @Test(expected = IllegalArgumentException.class) + public void testMissingName() throws Exception { + new Hero.HeroBuilder(Profession.THIEF, null); + } + + /** + * Test if the hero build by the builder has the correct attributes, as requested + */ + @Test + public void testBuildHero() throws Exception { + final String heroName = "Sir Lancelot"; + + final Hero hero = new Hero.HeroBuilder(Profession.WARRIOR, heroName) + .withArmor(Armor.CHAIN_MAIL) + .withWeapon(Weapon.SWORD) + .withHairType(HairType.LONG_CURLY) + .withHairColor(HairColor.BLOND) + .build(); + + assertNotNull(hero); + assertNotNull(hero.toString()); + assertEquals(Profession.WARRIOR, hero.getProfession()); + assertEquals(heroName, hero.getName()); + assertEquals(Armor.CHAIN_MAIL, hero.getArmor()); + assertEquals(Weapon.SWORD, hero.getWeapon()); + assertEquals(HairType.LONG_CURLY, hero.getHairType()); + assertEquals(HairColor.BLOND, hero.getHairColor()); + + } + +} \ No newline at end of file From 8367c83aca702025b8124b25009e7ac9d6d2705c Mon Sep 17 00:00:00 2001 From: Jeroen Meulemeester Date: Sun, 6 Dec 2015 23:48:48 +0100 Subject: [PATCH 3/4] Add proper unit tests for bridge pattern #293 --- bridge/pom.xml | 4 ++ .../bridge/BlindingMagicWeaponTest.java | 33 +++++++++++++++ .../bridge/FlyingMagicWeaponTest.java | 33 +++++++++++++++ .../com/iluwatar/bridge/MagicWeaponTest.java | 42 +++++++++++++++++++ .../bridge/SoulEatingMagicWeaponTest.java | 33 +++++++++++++++ 5 files changed, 145 insertions(+) create mode 100644 bridge/src/test/java/com/iluwatar/bridge/BlindingMagicWeaponTest.java create mode 100644 bridge/src/test/java/com/iluwatar/bridge/FlyingMagicWeaponTest.java create mode 100644 bridge/src/test/java/com/iluwatar/bridge/MagicWeaponTest.java create mode 100644 bridge/src/test/java/com/iluwatar/bridge/SoulEatingMagicWeaponTest.java diff --git a/bridge/pom.xml b/bridge/pom.xml index db2f0d340..10b763eab 100644 --- a/bridge/pom.xml +++ b/bridge/pom.xml @@ -14,5 +14,9 @@ junit test + + org.mockito + mockito-core + diff --git a/bridge/src/test/java/com/iluwatar/bridge/BlindingMagicWeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/BlindingMagicWeaponTest.java new file mode 100644 index 000000000..a7a2d1536 --- /dev/null +++ b/bridge/src/test/java/com/iluwatar/bridge/BlindingMagicWeaponTest.java @@ -0,0 +1,33 @@ +package com.iluwatar.bridge; + +import org.junit.Test; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/6/15 - 11:15 PM + * + * @author Jeroen Meulemeester + */ +public class BlindingMagicWeaponTest extends MagicWeaponTest { + + /** + * Invoke all possible actions on the weapon and check if the actions are executed on the actual + * underlying weapon implementation. + */ + @Test + public void testExcalibur() throws Exception { + final Excalibur excalibur = spy(new Excalibur()); + final BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(excalibur); + + testBasicWeaponActions(blindingMagicWeapon, excalibur); + + blindingMagicWeapon.blind(); + verify(excalibur, times(1)).blindImp(); + verifyNoMoreInteractions(excalibur); + } + +} diff --git a/bridge/src/test/java/com/iluwatar/bridge/FlyingMagicWeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/FlyingMagicWeaponTest.java new file mode 100644 index 000000000..55b89bb36 --- /dev/null +++ b/bridge/src/test/java/com/iluwatar/bridge/FlyingMagicWeaponTest.java @@ -0,0 +1,33 @@ +package com.iluwatar.bridge; + +import org.junit.Test; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/6/15 - 11:26 PM + * + * @author Jeroen Meulemeester + */ +public class FlyingMagicWeaponTest extends MagicWeaponTest { + + /** + * Invoke all possible actions on the weapon and check if the actions are executed on the actual + * underlying weapon implementation. + */ + @Test + public void testMjollnir() throws Exception { + final Mjollnir mjollnir = spy(new Mjollnir()); + final FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(mjollnir); + + testBasicWeaponActions(flyingMagicWeapon, mjollnir); + + flyingMagicWeapon.fly(); + verify(mjollnir, times(1)).flyImp(); + verifyNoMoreInteractions(mjollnir); + } + +} \ No newline at end of file diff --git a/bridge/src/test/java/com/iluwatar/bridge/MagicWeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/MagicWeaponTest.java new file mode 100644 index 000000000..eb7bfb34e --- /dev/null +++ b/bridge/src/test/java/com/iluwatar/bridge/MagicWeaponTest.java @@ -0,0 +1,42 @@ +package com.iluwatar.bridge; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/6/15 - 11:28 PM + * + * @author Jeroen Meulemeester + */ +public abstract class MagicWeaponTest { + + /** + * Invoke the basic actions of the given weapon, and test if the underlying weapon implementation + * is invoked + * + * @param weaponImpl The spied weapon implementation where actions are bridged to + * @param weapon The weapon, handled by the app + */ + protected final void testBasicWeaponActions(final MagicWeapon weapon, + final MagicWeaponImpl weaponImpl) { + assertNotNull(weapon); + assertNotNull(weaponImpl); + assertNotNull(weapon.getImp()); + + weapon.swing(); + verify(weaponImpl, times(1)).swingImp(); + verifyNoMoreInteractions(weaponImpl); + + weapon.wield(); + verify(weaponImpl, times(1)).wieldImp(); + verifyNoMoreInteractions(weaponImpl); + + weapon.unwield(); + verify(weaponImpl, times(1)).unwieldImp(); + verifyNoMoreInteractions(weaponImpl); + + } + +} diff --git a/bridge/src/test/java/com/iluwatar/bridge/SoulEatingMagicWeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/SoulEatingMagicWeaponTest.java new file mode 100644 index 000000000..2d9c24083 --- /dev/null +++ b/bridge/src/test/java/com/iluwatar/bridge/SoulEatingMagicWeaponTest.java @@ -0,0 +1,33 @@ +package com.iluwatar.bridge; + +import org.junit.Test; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/6/15 - 11:43 PM + * + * @author Jeroen Meulemeester + */ +public class SoulEatingMagicWeaponTest extends MagicWeaponTest { + + /** + * Invoke all possible actions on the weapon and check if the actions are executed on the actual + * underlying weapon implementation. + */ + @Test + public void testStormBringer() throws Exception { + final Stormbringer stormbringer = spy(new Stormbringer()); + final SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(stormbringer); + + testBasicWeaponActions(soulEatingMagicWeapon, stormbringer); + + soulEatingMagicWeapon.eatSoul(); + verify(stormbringer, times(1)).eatSoulImp(); + verifyNoMoreInteractions(stormbringer); + } + +} \ No newline at end of file From 10a94cc91cba699aef5f07f77631d40ccbdd1de7 Mon Sep 17 00:00:00 2001 From: Jeroen Meulemeester Date: Mon, 7 Dec 2015 20:23:52 +0100 Subject: [PATCH 4/4] Add proper unit tests for decorator pattern --- decorator/pom.xml | 4 ++ .../iluwatar/decorator/SmartTrollTest.java | 38 +++++++++++ .../com/iluwatar/decorator/TrollTest.java | 66 +++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 decorator/src/test/java/com/iluwatar/decorator/SmartTrollTest.java create mode 100644 decorator/src/test/java/com/iluwatar/decorator/TrollTest.java diff --git a/decorator/pom.xml b/decorator/pom.xml index ab15f0e01..272f54a9b 100644 --- a/decorator/pom.xml +++ b/decorator/pom.xml @@ -14,5 +14,9 @@ junit test + + org.mockito + mockito-core + diff --git a/decorator/src/test/java/com/iluwatar/decorator/SmartTrollTest.java b/decorator/src/test/java/com/iluwatar/decorator/SmartTrollTest.java new file mode 100644 index 000000000..fd73d91cb --- /dev/null +++ b/decorator/src/test/java/com/iluwatar/decorator/SmartTrollTest.java @@ -0,0 +1,38 @@ +package com.iluwatar.decorator; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/7/15 - 7:47 PM + * + * @author Jeroen Meulemeester + */ +public class SmartTrollTest { + + @Test + public void testSmartTroll() throws Exception { + // Create a normal troll first, but make sure we can spy on it later on. + final Hostile simpleTroll = spy(new Troll()); + + // Now we want to decorate the troll to make it smarter ... + final Hostile smartTroll = new SmartTroll(simpleTroll); + assertEquals(30, smartTroll.getAttackPower()); + verify(simpleTroll, times(1)).getAttackPower(); + + // Check if the smart troll actions are delegated to the decorated troll + smartTroll.attack(); + verify(simpleTroll, times(1)).attack(); + + smartTroll.fleeBattle(); + verify(simpleTroll, times(1)).fleeBattle(); + verifyNoMoreInteractions(simpleTroll); + + } + +} diff --git a/decorator/src/test/java/com/iluwatar/decorator/TrollTest.java b/decorator/src/test/java/com/iluwatar/decorator/TrollTest.java new file mode 100644 index 000000000..021f7ed1a --- /dev/null +++ b/decorator/src/test/java/com/iluwatar/decorator/TrollTest.java @@ -0,0 +1,66 @@ +package com.iluwatar.decorator; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.PrintStream; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/7/15 - 7:26 PM + * + * @author Jeroen Meulemeester + */ +public class TrollTest { + + /** + * The mocked standard out stream, required since the actions don't have any influence on other + * objects, except for writing to the std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + @Test + public void testTrollActions() throws Exception { + final Troll troll = new Troll(); + assertEquals(10, troll.getAttackPower()); + + troll.attack(); + verify(this.stdOutMock, times(1)).println(eq("The troll swings at you with a club!")); + + troll.fleeBattle(); + verify(this.stdOutMock, times(1)).println(eq("The troll shrieks in horror and runs away!")); + + verifyNoMoreInteractions(this.stdOutMock); + } + +} \ No newline at end of file