diff --git a/solutions/java/pom.xml b/solutions/java/pom.xml
new file mode 100644
index 00000000..6b9c3bcd
--- /dev/null
+++ b/solutions/java/pom.xml
@@ -0,0 +1,35 @@
+
+ 4.0.0
+ coffeevendingmachine
+ coffeevendingmachine
+ 1.0-SNAPSHOT
+ jar
+ Coffee Vending Machine
+
+ 1.8
+ 1.8
+ 5.10.2
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ ${junit.jupiter.version}
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.2.5
+
+ false
+
+
+
+
+
diff --git a/solutions/java/src/coffeevendingmachine/CoffeeVendingMachine.java b/solutions/java/src/coffeevendingmachine/CoffeeVendingMachine.java
index 8b50a5a1..b4c25342 100644
--- a/solutions/java/src/coffeevendingmachine/CoffeeVendingMachine.java
+++ b/solutions/java/src/coffeevendingmachine/CoffeeVendingMachine.java
@@ -73,4 +73,8 @@ public void showIngredients() {
System.out.println("Ingredient Levels:");
ingredientStore.getAllIngredients().forEach((k, v) -> System.out.println(k + ": " + v));
}
+
+ public Map showIngredientsMap() {
+ return ingredientStore.getAllIngredients();
+ }
}
diff --git a/solutions/java/test/coffeevendingmachine/CoffeeVendingMachineTest.java b/solutions/java/test/coffeevendingmachine/CoffeeVendingMachineTest.java
new file mode 100644
index 00000000..34b3d03a
--- /dev/null
+++ b/solutions/java/test/coffeevendingmachine/CoffeeVendingMachineTest.java
@@ -0,0 +1,116 @@
+package coffeevendingmachine;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.DisplayName;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Map;
+
+public class CoffeeVendingMachineTest {
+
+ private CoffeeVendingMachine machine;
+ private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ private final PrintStream originalOut = System.out;
+
+ @BeforeEach
+ void setUp() {
+ machine = CoffeeVendingMachine.getInstance();
+ System.setOut(new PrintStream(outputStream));
+
+ // Reset and refill ingredients for each test
+ machine.refillIngredient("Water", 150);
+ machine.refillIngredient("Coffee", 100);
+ machine.refillIngredient("Milk", 100);
+ }
+
+ @Test
+ @DisplayName("Test coffee selection")
+ void testSelectCoffee() {
+ CoffeeRecipe espresso = machine.selectCoffee("Espresso");
+ assertEquals("Espresso", espresso.getName());
+ assertEquals(2.5, espresso.getPrice());
+ assertEquals(50, espresso.getRecipe().get("Water"));
+ assertEquals(20, espresso.getRecipe().get("Coffee"));
+ }
+
+ @Test
+ @DisplayName("Test invalid coffee selection")
+ void testInvalidCoffeeSelection() {
+ Exception exception = assertThrows(RuntimeException.class, () -> {
+ machine.selectCoffee("MochaCoffee");
+ });
+
+ String expectedMessage = "Invalid coffee recipe: MochaCoffee";
+ String actualMessage = exception.getMessage();
+
+ assertTrue(actualMessage.contains(expectedMessage));
+ }
+
+ @Test
+ @DisplayName("Test successful coffee dispensing")
+ void testDispenseCoffee() {
+ CoffeeRecipe latte = machine.selectCoffee("Latte");
+ machine.dispenseCoffee(latte, new Payment(4.0));
+
+ String output = outputStream.toString();
+ assertTrue(output.contains("Dispensing: Latte"));
+ assertTrue(output.contains("Processing Payment"));
+ assertTrue(output.contains("Please collect your change: $1.0"));
+ }
+
+ @Test
+ @DisplayName("Test insufficient payment")
+ void testInsufficientPayment() {
+ CoffeeRecipe cappuccino = machine.selectCoffee("Cappuccino");
+
+ Exception exception = assertThrows(RuntimeException.class, () -> {
+ machine.dispenseCoffee(cappuccino, new Payment(2.0));
+ });
+
+ String expectedMessage = "Insufficient payment for Cappuccino";
+ String actualMessage = exception.getMessage();
+
+ assertTrue(actualMessage.contains(expectedMessage));
+ }
+
+
+
+ @Test
+ @DisplayName("Test ingredient consumption")
+ void testIngredientConsumption() {
+ CoffeeRecipe latte = machine.selectCoffee("Latte");
+
+ // Check initial levels
+ Map beforeLevels = machine.showIngredientsMap();
+ int initialWaterLevel = beforeLevels.get("Water");
+ int initialCoffeeLevel = beforeLevels.get("Coffee");
+ int initialMilkLevel = beforeLevels.get("Milk");
+
+ machine.dispenseCoffee(latte, new Payment(3.0));
+
+ // Check levels after dispensing
+ Map afterLevels = machine.showIngredientsMap();
+ assertEquals(initialWaterLevel - 50, afterLevels.get("Water").intValue());
+ assertEquals(initialCoffeeLevel - 20, afterLevels.get("Coffee").intValue());
+ assertEquals(initialMilkLevel - 30, afterLevels.get("Milk").intValue());
+ }
+
+ @Test
+ @DisplayName("Test ingredient refill")
+ void testIngredientRefill() {
+ // First check initial level
+ Map beforeLevels = machine.showIngredientsMap();
+ int initialWaterLevel = beforeLevels.get("Water");
+
+ // Add more water
+ machine.refillIngredient("Water", 50);
+
+ // Check after refill
+ Map afterLevels = machine.showIngredientsMap();
+ assertEquals(initialWaterLevel + 50, afterLevels.get("Water").intValue());
+ }
+}