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
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
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
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