diff --git a/docs/Readme.md b/docs/Readme.md new file mode 100644 index 000000000..64ed85f39 --- /dev/null +++ b/docs/Readme.md @@ -0,0 +1,34 @@ +## 기능 목록 + +- InputView + - [x] 자판기가 보유하고 있는 금액 입력 + - [x] 상품명, 가격, 수량을 입력 + - [x] 구매할 상품명 입력 + +- MakeRandomCoin + - [x] 자판기가 보유하고 있는 금액으로 동전을 무작위로 생성 + +- product + - [x] 상품 정보 저장 + - [x] 상품 저장 형식이 옳지 않으면 오류 발생 + +- Products + - [x] 상품명, 가격, 수량은 쉼표로, 개별 상품은 대괄호([])로 묶어 세미콜론(;)으로 구분한다. + - [x] 상품 가격은 100원부터 시작하며, 10원으로 나누어떨어져야 한다. + - [x] 상품 최소 가격이 입력 가격 보다 크면 오류 발생 + - [x] 상품리스트에 없는 상품이름을 입력하면 오류 발생 + +- VendingMachine + - [x] 보유 금액, 상품 저장 + - [x] 잔돈 반환 + +- CalculatorService + - [x] 남은 금액이 상품의 최저 가격보다 적거나, 모든 상품이 소진된 경우 바로 잔돈을 돌려준다. + - [x] 잔돈을 돌려줄 때 현재 보유한 최소 개수의 동전으로 잔돈을 돌려준다. + - [x] 잔돈을 반환할 수 없는 경우 잔돈으로 반환할 수 있는 금액만 반환한다. + - [x] 반환되지 않은 금액은 자판기에 남는다. + +## 에러 처리 +- [x] 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 해당 부분부터 다시 입력을 받는다. +- [x] 상품 저장 형식 에러 처리 +- [x] 자판기 보유 금액 에러 처리(숫자, 최솟값) \ No newline at end of file diff --git a/src/main/java/vendingmachine/Application.java b/src/main/java/vendingmachine/Application.java index 9d3be447b..a918e8d40 100644 --- a/src/main/java/vendingmachine/Application.java +++ b/src/main/java/vendingmachine/Application.java @@ -1,7 +1,11 @@ package vendingmachine; +import vendingmachine.controller.MachineController; + public class Application { public static void main(String[] args) { // TODO: 프로그램 구현 + MachineController machineController = new MachineController(); + machineController.run(); } } diff --git a/src/main/java/vendingmachine/Coin.java b/src/main/java/vendingmachine/Coin.java deleted file mode 100644 index c76293fbc..000000000 --- a/src/main/java/vendingmachine/Coin.java +++ /dev/null @@ -1,16 +0,0 @@ -package vendingmachine; - -public enum Coin { - COIN_500(500), - COIN_100(100), - COIN_50(50), - COIN_10(10); - - private final int amount; - - Coin(final int amount) { - this.amount = amount; - } - - // 추가 기능 구현 -} diff --git a/src/main/java/vendingmachine/constants/Coin.java b/src/main/java/vendingmachine/constants/Coin.java new file mode 100644 index 000000000..268f069c8 --- /dev/null +++ b/src/main/java/vendingmachine/constants/Coin.java @@ -0,0 +1,42 @@ +package vendingmachine.constants; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public enum Coin { + COIN_500(500), + COIN_100(100), + COIN_50(50), + COIN_10(10); + + private final int amount; + + Coin(final int amount) { + this.amount = amount; + } + + // 추가 기능 구현 + public static List coinPrices() { + return Stream.of(Coin.values()) + .map(coin -> coin.amount) + .collect(Collectors.toList()); + } + + public static Coin getByAmount(Integer coinAmount) { + return Stream.of(Coin.values()) + .filter(coin -> coin.amount == coinAmount) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(ErrorMessage.NOT_FOUND_COIN.getMessage())); + + } + + public int getAmount() { + return amount; + } + + @Override + public String toString() { + return String.valueOf(amount); + } +} diff --git a/src/main/java/vendingmachine/constants/ErrorMessage.java b/src/main/java/vendingmachine/constants/ErrorMessage.java new file mode 100644 index 000000000..49b3e328f --- /dev/null +++ b/src/main/java/vendingmachine/constants/ErrorMessage.java @@ -0,0 +1,23 @@ +package vendingmachine.constants; + +public enum ErrorMessage { + MACHINE_COIN_NUMERIC_ERROR("숫자를 입력해 주세요"), + MIN_MACHINE_AMOUNT_HELD_ERROR("자판기 보유 금액은 최소 100원 부터 시작해주세요"), + NOT_FOUND_COIN("존재 하지 않는 Coin 입니다."), + PRODUCT_REGEX_ERROR("일치하지 않는 상품 형식입니다."), + PRODUCT_PRICE_ERROR("상품 가격은 100원부터 시작하며, 10원으로 나누어떨어져야 합니다."), + INPUT_MONEY_ERROR("입력한 금액이 너무 적습니다."), + INPUT_PRODUCT_NAME_ERROR("존재하지 않는 상품명 입니다."); + + + private static final String ERROR = "[ERROR] "; + private final String message; + + ErrorMessage(String message) { + this.message = message; + } + + public String getMessage() { + return ERROR + message; + } +} diff --git a/src/main/java/vendingmachine/constants/ProgressMessage.java b/src/main/java/vendingmachine/constants/ProgressMessage.java new file mode 100644 index 000000000..42088e149 --- /dev/null +++ b/src/main/java/vendingmachine/constants/ProgressMessage.java @@ -0,0 +1,22 @@ +package vendingmachine.constants; + +public enum ProgressMessage { + INPUT_MACHINE_AMOUNT_HELD("자판기가 보유하고 있는 금액을 입력해 주세요."), + MACHINE_AMOUNT_HELD("자판기가 보유한 동전"), + INPUT_PRODUCTS_DETAIL("상품명과 가격, 수량을 입력해 주세요."), + INPUT_MONEY("투입 금액을 입력해 주세요."), + INPUT_BUY_PRODUCT_NAME("구매할 상품명을 입력해 주세요."), + CUR_INPUT_MONEY("투입 금액: %d원"), + RETURN_CHANGE("잔돈"); + + private final String message; + + ProgressMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return message; + } +} diff --git a/src/main/java/vendingmachine/constants/ScreenElement.java b/src/main/java/vendingmachine/constants/ScreenElement.java new file mode 100644 index 000000000..d49245308 --- /dev/null +++ b/src/main/java/vendingmachine/constants/ScreenElement.java @@ -0,0 +1,18 @@ +package vendingmachine.constants; + +public enum ScreenElement { + COIN_UNIT("원"), + DIVISION(" - "), + COUNT_UNIT("개"); + + private final String value; + + ScreenElement(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/src/main/java/vendingmachine/controller/MachineController.java b/src/main/java/vendingmachine/controller/MachineController.java new file mode 100644 index 000000000..b15ef9fd2 --- /dev/null +++ b/src/main/java/vendingmachine/controller/MachineController.java @@ -0,0 +1,49 @@ +package vendingmachine.controller; + +import vendingmachine.domain.Products; +import vendingmachine.domain.VendingMachine; +import vendingmachine.service.CalculatorService; +import vendingmachine.view.InputView; +import vendingmachine.view.OutputView; + +public class MachineController { + + private final InputView inputView; + private final OutputView outputView; + private final VendingMachine vendingMachine; + private final CalculatorService calculatorService; + + public MachineController() { + this.inputView = new InputView(); + this.outputView = new OutputView(); + this.vendingMachine = new VendingMachine(); + this.calculatorService = new CalculatorService(vendingMachine); + } + + public void run() { + initVendingMachineAmountHeld(); + + Products products = inputView.inputProducts(); + vendingMachine.registerProducts(products); + + calculatorService.receiveMoney(inputView.inputMoney(products)); + buyProducts(products); + } + + private void initVendingMachineAmountHeld() { + int amountHeld = inputView.inputMachineAmountHeld(); + vendingMachine.saveRandomCoin(amountHeld); + outputView.printMachineAmountHeld(vendingMachine.showRandomCoins()); + } + + private void buyProducts(Products products) { + while (true) { + outputView.printCurInputMoney(calculatorService.getInputMoney()); + if (calculatorService.isBuyingProducts()) break; + + String name = inputView.inputBuyProduct(products); + calculatorService.buyProducts(name); + } + outputView.printResultChange(calculatorService.returnChange()); + } +} diff --git a/src/main/java/vendingmachine/domain/MakeRandomCoin.java b/src/main/java/vendingmachine/domain/MakeRandomCoin.java new file mode 100644 index 000000000..2b1a4e45e --- /dev/null +++ b/src/main/java/vendingmachine/domain/MakeRandomCoin.java @@ -0,0 +1,42 @@ +package vendingmachine.domain; + +import camp.nextstep.edu.missionutils.Randoms; +import vendingmachine.constants.Coin; + +import java.util.ArrayList; +import java.util.List; + +public class MakeRandomCoin { + private final List randomCoins = new ArrayList<>(); + + public void makeCoins(int amount) { + while (isMakingRandomCoins(amount)) { + randomCoins.add(randomCoin()); + removeOverCoin(amount); + } + } + + private boolean isMakingRandomCoins(int amount) { + return getSum(randomCoins) != amount; + } + + private int randomCoin() { + return Randoms.pickNumberInList(Coin.coinPrices()); + } + + private void removeOverCoin(int amount) { + if (getSum(randomCoins) > amount) { + randomCoins.remove(randomCoins.size() - 1); + } + } + + private int getSum(List coins) { + return coins.stream() + .mapToInt(Integer::intValue) + .sum(); + } + + public List getRandomCoins() { + return randomCoins; + } +} diff --git a/src/main/java/vendingmachine/domain/Product.java b/src/main/java/vendingmachine/domain/Product.java new file mode 100644 index 000000000..ab3ce802b --- /dev/null +++ b/src/main/java/vendingmachine/domain/Product.java @@ -0,0 +1,41 @@ +package vendingmachine.domain; + +import vendingmachine.constants.ErrorMessage; + +public class Product { + + private static final int MIN_PRICE = 100; + private static final int COIN_UNIT = 10; + private final String name; + private final int price; + private final int amount; + + public Product(String name, int price, int amount) { + validatePriceRule(price); + this.name = name; + this.price = price; + this.amount = amount; + } + + private static boolean isaBreakPriceRule(int price) { + return price < MIN_PRICE || price % COIN_UNIT != 0; + } + + private void validatePriceRule(int price) { + if (isaBreakPriceRule(price)) { + throw new IllegalArgumentException(ErrorMessage.PRODUCT_PRICE_ERROR.getMessage()); + } + } + + public int getPrice() { + return price; + } + + public String getName() { + return name; + } + + public int getAmount() { + return amount; + } +} diff --git a/src/main/java/vendingmachine/domain/Products.java b/src/main/java/vendingmachine/domain/Products.java new file mode 100644 index 000000000..3871a2416 --- /dev/null +++ b/src/main/java/vendingmachine/domain/Products.java @@ -0,0 +1,61 @@ +package vendingmachine.domain; + +import vendingmachine.constants.ErrorMessage; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class Products { + + private static final String PRODUCT_DIVISION = ";"; + private static final String PRODUCT_DETAIL_DIVISION = ","; + private final List products; + + + public Products(String productData) { + products = Arrays.stream(productData.split(PRODUCT_DIVISION)) + .map(productToken -> productToken.substring(1, productToken.length() - 1).split(PRODUCT_DETAIL_DIVISION)) + .map(productDetails -> new Product(productDetails[0], Integer.parseInt(productDetails[1]), Integer.parseInt(productDetails[2]))) + .collect(Collectors.toList()); + } + + public void validateInputMoney(String money) { + int price = Integer.parseInt(money); + if (getMinProductPrice() > price) { + throw new IllegalArgumentException(ErrorMessage.INPUT_MONEY_ERROR.getMessage()); + } + } + + public void validateInputProductName(String name) { + if (products.stream().noneMatch(product -> product.getName().equals(name))) { + throw new IllegalArgumentException(ErrorMessage.INPUT_PRODUCT_NAME_ERROR.getMessage()); + } + } + + public boolean isBuyingProducts(int inputMoney) { + return inputMoney < getMinProductPrice() || isProductSoldOut(); + } + + private int getMinProductPrice() { + return products.stream() + .mapToInt(Product::getPrice) + .min() + .orElse(Integer.MAX_VALUE); + } + + private boolean isProductSoldOut() { + int amount = products.stream() + .mapToInt(Product::getAmount) + .sum(); + return amount == 0; + } + + public int getProductPrice(String name) { + return products.stream() + .filter(product -> product.getName().equals(name)) + .findFirst() + .map(Product::getPrice) + .orElse(Integer.MAX_VALUE); + } +} diff --git a/src/main/java/vendingmachine/domain/VendingMachine.java b/src/main/java/vendingmachine/domain/VendingMachine.java new file mode 100644 index 000000000..ae15715cc --- /dev/null +++ b/src/main/java/vendingmachine/domain/VendingMachine.java @@ -0,0 +1,62 @@ +package vendingmachine.domain; + +import vendingmachine.constants.Coin; +import vendingmachine.constants.ScreenElement; + +import java.util.EnumMap; +import java.util.List; +import java.util.stream.Collectors; + +public class VendingMachine { + private final EnumMap coins = new EnumMap<>(Coin.class); + private final MakeRandomCoin makeRandomCoin = new MakeRandomCoin(); + private Products products; + + public VendingMachine() { + for (Coin coin : Coin.values()) { + coins.put(coin, 0); + } + } + + public void saveRandomCoin(int amount) { + makeRandomCoin.makeCoins(amount); + List randomCoins = makeRandomCoin.getRandomCoins(); + + coins.putAll(randomCoins.stream() + .collect(Collectors.groupingBy( + Coin::getByAmount, + Collectors.summingInt(coinAmount -> 1) + ))); + } + + public String showRandomCoins() { + return coins.entrySet().stream() + .map(entry -> entry.getKey().toString() + ScreenElement.COIN_UNIT + + ScreenElement.DIVISION + entry.getValue() + ScreenElement.COUNT_UNIT) + .collect(Collectors.joining("\n")); + } + + public EnumMap returnChange(int inputMoney) { + int remainingChange = inputMoney; + EnumMap changeCoins = new EnumMap<>(Coin.class); + + for (Coin coin : Coin.values()) { + int coinValue = coin.getAmount(); + int coinCount = coins.get(coin); + int changeCount = Math.min(coinCount, remainingChange / coinValue); + + coins.put(coin, coins.get(coin) - changeCount); + changeCoins.put(coin, changeCount); + remainingChange -= changeCount * coinValue; + } + return changeCoins; + } + + public void registerProducts(Products products) { + this.products = products; + } + + public Products getProducts() { + return products; + } +} diff --git a/src/main/java/vendingmachine/service/CalculatorService.java b/src/main/java/vendingmachine/service/CalculatorService.java new file mode 100644 index 000000000..8f4a4457c --- /dev/null +++ b/src/main/java/vendingmachine/service/CalculatorService.java @@ -0,0 +1,50 @@ +package vendingmachine.service; + +import vendingmachine.constants.Coin; +import vendingmachine.constants.ScreenElement; +import vendingmachine.domain.Products; +import vendingmachine.domain.VendingMachine; + +import java.util.EnumMap; +import java.util.stream.Collectors; + +public class CalculatorService { + + private final VendingMachine vendingMachine; + private int inputMoney; + + public CalculatorService(VendingMachine vendingMachine) { + this.vendingMachine = vendingMachine; + } + + public void receiveMoney(int inputMoney) { + this.inputMoney = inputMoney; + } + + public boolean isBuyingProducts() { + Products products = vendingMachine.getProducts(); + return products.isBuyingProducts(inputMoney); + } + + public void buyProducts(String name) { + Products products = vendingMachine.getProducts(); + int productPrice = products.getProductPrice(name); + inputMoney -= productPrice; + } + + public String returnChange() { + EnumMap changeCoins = vendingMachine.returnChange(inputMoney); + + return changeCoins.entrySet().stream() + .filter(entry -> entry.getValue() != 0) + .map(entry -> entry.getKey().toString() + ScreenElement.COIN_UNIT + + ScreenElement.DIVISION + entry.getValue() + + ScreenElement.COUNT_UNIT) + .collect(Collectors.joining("\n")); + } + + public int getInputMoney() { + return inputMoney; + } + +} diff --git a/src/main/java/vendingmachine/view/InputValidator.java b/src/main/java/vendingmachine/view/InputValidator.java new file mode 100644 index 000000000..0c546aaaf --- /dev/null +++ b/src/main/java/vendingmachine/view/InputValidator.java @@ -0,0 +1,28 @@ +package vendingmachine.view; + +import vendingmachine.constants.ErrorMessage; + +public class InputValidator { + private static final String NUMERIC_REGEX = "^[0-9]+$"; + private static final int MIN_MACHINE_AMOUNT_HELD = 100; + private static final String PRODUCT_PATTERN_REGEX = "\\[[^\\[\\]]+,\\d+,\\d+\\](;\\[[^\\[\\]]+,\\d+,\\d+\\])*"; + + public void validateMachineAmountHeld(String amount) { + validateNumeric(amount); + if (Integer.parseInt(amount) < MIN_MACHINE_AMOUNT_HELD) { + throw new IllegalArgumentException(ErrorMessage.MIN_MACHINE_AMOUNT_HELD_ERROR.getMessage()); + } + } + + public void validateNumeric(String amount) { + if (!amount.matches(NUMERIC_REGEX)) { + throw new IllegalArgumentException(ErrorMessage.MACHINE_COIN_NUMERIC_ERROR.getMessage()); + } + } + + public void validateDivisionProducts(String products) { + if (!products.matches(PRODUCT_PATTERN_REGEX)) { + throw new IllegalArgumentException(ErrorMessage.PRODUCT_REGEX_ERROR.getMessage()); + } + } +} diff --git a/src/main/java/vendingmachine/view/InputView.java b/src/main/java/vendingmachine/view/InputView.java new file mode 100644 index 000000000..66fbe6d5f --- /dev/null +++ b/src/main/java/vendingmachine/view/InputView.java @@ -0,0 +1,64 @@ +package vendingmachine.view; + +import camp.nextstep.edu.missionutils.Console; +import vendingmachine.domain.Products; + +import java.util.function.Supplier; + +public class InputView { + + private final OutputView outputView = new OutputView(); + private final InputValidator inputValidator = new InputValidator(); + + public Integer inputMachineAmountHeld() { + return read(() -> { + outputView.printInputMachineAmountHeld(); + String amount = Console.readLine(); + inputValidator.validateMachineAmountHeld(amount); + outputView.printEnter(); + return Integer.parseInt(amount); + }); + } + + public Products inputProducts() { + return read(() -> { + outputView.printInputProductsDetail(); + String products = Console.readLine(); + inputValidator.validateDivisionProducts(products); + outputView.printEnter(); + return new Products(products); + }); + } + + public Integer inputMoney(Products products) { + return read(() -> { + outputView.printInputMoney(); + String money = Console.readLine(); + inputValidator.validateNumeric(money); + products.validateInputMoney(money); + outputView.printEnter(); + return Integer.parseInt(money); + }); + } + + public String inputBuyProduct(Products products) { + return read(() -> { + outputView.printInputBuyProductName(); + String name = Console.readLine(); + products.validateInputProductName(name); + outputView.printEnter(); + return name; + }); + } + + + private T read(Supplier input) { + while (true) { + try { + return input.get(); + } catch (IllegalArgumentException e) { + outputView.printError(e.getMessage()); + } + } + } +} diff --git a/src/main/java/vendingmachine/view/OutputView.java b/src/main/java/vendingmachine/view/OutputView.java new file mode 100644 index 000000000..a9c16f0d9 --- /dev/null +++ b/src/main/java/vendingmachine/view/OutputView.java @@ -0,0 +1,44 @@ +package vendingmachine.view; + +import vendingmachine.constants.ProgressMessage; + +public class OutputView { + + public void printError(String error) { + System.out.println(error); + } + + public void printInputMachineAmountHeld() { + System.out.println(ProgressMessage.INPUT_MACHINE_AMOUNT_HELD); + } + + public void printInputProductsDetail() { + System.out.println(ProgressMessage.INPUT_PRODUCTS_DETAIL); + } + + public void printEnter() { + System.out.println(); + } + + public void printMachineAmountHeld(String screen) { + System.out.println(ProgressMessage.MACHINE_AMOUNT_HELD); + System.out.println(screen); + } + + public void printInputMoney() { + System.out.println(ProgressMessage.INPUT_MONEY); + } + + public void printInputBuyProductName() { + System.out.println(ProgressMessage.INPUT_BUY_PRODUCT_NAME); + } + + public void printCurInputMoney(int money) { + System.out.printf((ProgressMessage.CUR_INPUT_MONEY) + "%n", money); + } + + public void printResultChange(String returnChange) { + System.out.println(ProgressMessage.RETURN_CHANGE); + System.out.println(returnChange); + } +} diff --git a/src/test/java/vendingmachine/CoinTest.java b/src/test/java/vendingmachine/CoinTest.java new file mode 100644 index 000000000..27fbf9d84 --- /dev/null +++ b/src/test/java/vendingmachine/CoinTest.java @@ -0,0 +1,26 @@ +package vendingmachine; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import vendingmachine.constants.Coin; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class CoinTest { + + @Test + @DisplayName("코인 가격들을 리스트로 반환한다.") + void getCoinPrices(){ + List coins = Coin.coinPrices(); + assertThat(coins).contains(500, 100, 50, 10); + } + + @Test + @DisplayName("코인 가격에 맞는 Coin을 반환한다.") + void getCoinByPrice(){ + Coin coin = Coin.getByAmount(500); + assertThat(coin).isEqualTo(Coin.COIN_500); + } +} diff --git a/src/test/java/vendingmachine/InputValidatorTest.java b/src/test/java/vendingmachine/InputValidatorTest.java new file mode 100644 index 000000000..f4c35e3e2 --- /dev/null +++ b/src/test/java/vendingmachine/InputValidatorTest.java @@ -0,0 +1,38 @@ +package vendingmachine; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import vendingmachine.view.InputValidator; + + +import static org.assertj.core.api.Assertions.*; + +public class InputValidatorTest { + + InputValidator inputValidator = new InputValidator(); + private static final String ERROR = "[ERROR] "; + + @Test + @DisplayName("자판기가 보유한 금액이 100원 이상 입력하지 않으면 에러가 발생한다.") + void validateMachineAmountHeld(){ + assertThatThrownBy(() -> inputValidator.validateMachineAmountHeld("50")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR); + } + + @Test + @DisplayName("자판기가 보유한 금액이 숫자가 않으면 에러가 발생한다.") + void validateNumeric(){ + assertThatThrownBy(() -> inputValidator.validateNumeric("ㄱㄱ")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR); + } + + @Test + @DisplayName("상품저장 형식이 옳지 않으면 에러가 발생한다.") + void validateDivisionProducts(){ + assertThatThrownBy(() -> inputValidator.validateDivisionProducts("ㄱㄱ")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR); + } +} diff --git a/src/test/java/vendingmachine/MakeRandomCoinTest.java b/src/test/java/vendingmachine/MakeRandomCoinTest.java new file mode 100644 index 000000000..848e4903a --- /dev/null +++ b/src/test/java/vendingmachine/MakeRandomCoinTest.java @@ -0,0 +1,24 @@ +package vendingmachine; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import vendingmachine.domain.MakeRandomCoin; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +public class MakeRandomCoinTest { + + MakeRandomCoin makeRandomCoin = new MakeRandomCoin(); + + @Test + @DisplayName("랜덤된 Coin을 저장한다.") + void saveRandomCoin(){ + makeRandomCoin.makeCoins(450); + + List coins = makeRandomCoin.getRandomCoins(); + + assertThat(coins).containsAnyOf(500, 100, 50, 10); + } +} diff --git a/src/test/java/vendingmachine/ProductTest.java b/src/test/java/vendingmachine/ProductTest.java new file mode 100644 index 000000000..aa70deadb --- /dev/null +++ b/src/test/java/vendingmachine/ProductTest.java @@ -0,0 +1,20 @@ +package vendingmachine; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import vendingmachine.domain.Product; + +import static org.assertj.core.api.Assertions.*; + +public class ProductTest { + + private static final String ERROR = "[ERROR] "; + + @Test + @DisplayName("상품 가격은 100원부터 시작하며, 10원으로 나누어떨어지지 않으면 에러가 발생한다.") + void validateProductRule(){ + assertThatThrownBy(() -> new Product("콜라", 455, 20)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR); + } +} diff --git a/src/test/java/vendingmachine/ProductsTest.java b/src/test/java/vendingmachine/ProductsTest.java new file mode 100644 index 000000000..c80a2b2a7 --- /dev/null +++ b/src/test/java/vendingmachine/ProductsTest.java @@ -0,0 +1,37 @@ +package vendingmachine; + + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import vendingmachine.domain.Products; + +import static org.assertj.core.api.Assertions.*; + +public class ProductsTest { + + private static final String ERROR = "[ERROR] "; + Products products = new Products("[콜라,1500,20];[사이다,1000,10]"); + @Test + @DisplayName("상품이 저장되는지 확인한다.") + void saveProducts(){ + int price = products.getProductPrice("콜라"); + + assertThat(price).isEqualTo(1500); + } + + @Test + @DisplayName("상품의 최저 가격보다 낮은 돈을 입력하면 에러가 발생한다..") + void validateInputMoney(){ + assertThatThrownBy(() -> products.validateInputMoney("500")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR); + } + + @Test + @DisplayName("저장된 상품의 이름과 일치하지 않으면 에러가 발생한다.") + void validateInputProductName(){ + assertThatThrownBy(() -> products.validateInputProductName("환타")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining(ERROR); + } +} diff --git a/src/test/java/vendingmachine/VendingMachineTest.java b/src/test/java/vendingmachine/VendingMachineTest.java new file mode 100644 index 000000000..2db6b3725 --- /dev/null +++ b/src/test/java/vendingmachine/VendingMachineTest.java @@ -0,0 +1,35 @@ +package vendingmachine; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import vendingmachine.constants.Coin; +import vendingmachine.domain.VendingMachine; + +import java.util.EnumMap; + +import static org.assertj.core.api.Assertions.*; +public class VendingMachineTest { + + VendingMachine vendingMachine = new VendingMachine(); + @Test + @DisplayName("랜덤으로 생성된 동전을 확인 할 수 있다.") + void showRandomCoins(){ + vendingMachine.saveRandomCoin(30); + + String screen = vendingMachine.showRandomCoins(); + + assertThat(screen).containsAnyOf("500원 - 0개", "100원 - 0개", "50원 - 0개", "10원 - 3개"); + } + + @Test + @DisplayName("자판기가 잔돈을 최소의 개수로 반환할 수 있다.") + void getMinCountChange(){ + vendingMachine.saveRandomCoin(30); + EnumMap changeCoins = vendingMachine.returnChange(30); + + int amount = changeCoins.get(Coin.COIN_10); + + assertThat(amount).isEqualTo(3); + + } +}