diff --git a/build.gradle b/build.gradle index 87254a3a..8102a907 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,7 @@ repositories { dependencies { testImplementation platform('org.junit:junit-bom:5.9.1') testImplementation('org.junit.jupiter:junit-jupiter') + testImplementation('org.assertj:assertj-core:3.24.2') } test { diff --git a/src/main/java/Calc.java b/src/main/java/Calc.java new file mode 100644 index 00000000..381c6173 --- /dev/null +++ b/src/main/java/Calc.java @@ -0,0 +1,24 @@ +public class Calc { + public int add(int a, int b) { + int res = a + b; + return res; + } + + public int minus(int a, int b) { + int res = a - b; + return res; + } + + public int times(int a, int b) { + int res = a * b; + return res; + } + + public int divide(int a, int b) { + if (b == 0) { + throw new IllegalArgumentException("0 으로 나눌 수 없습니다."); + } + int res = a / b; + return res; + } +} \ No newline at end of file diff --git a/src/main/java/StringCalc.java b/src/main/java/StringCalc.java new file mode 100644 index 00000000..55af9585 --- /dev/null +++ b/src/main/java/StringCalc.java @@ -0,0 +1,78 @@ +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class StringCalc { + + private static final String DEFAULT_DELIMITERS = "[,:]"; + + public int add(String text) { + if (isNullOrEmpty(text)) { + return 0; + } + + DelimiterInfo delimiterInfo = parseDelimiterInfo(text); + String[] numbersAsString = splitByDelimiter(delimiterInfo.textToParse, delimiterInfo.delimiter); + + return calculateSum(numbersAsString); + } + + private boolean isNullOrEmpty(String text) { + return text == null || text.isEmpty(); + } + + private DelimiterInfo parseDelimiterInfo(String text) { + Pattern customDelimiterPattern = Pattern.compile("//(.)\n(.*)"); + Matcher matcher = customDelimiterPattern.matcher(text); + + if (matcher.find()) { + String customDelimiter = Pattern.quote(matcher.group(1)); + String actualTextToParse = matcher.group(2); + // 커스텀 구분자와 기본 구분자를 모두 포함 + String combinedDelimiters = DEFAULT_DELIMITERS + "|" + customDelimiter; + return new DelimiterInfo(combinedDelimiters, actualTextToParse); + } + + return new DelimiterInfo(DEFAULT_DELIMITERS, text); + } + + private String[] splitByDelimiter(String text, String delimiter) { + return text.split(delimiter); + } + + private int calculateSum(String[] numbersAsString) { + int sum = 0; + for (String numStr : numbersAsString) { + if (numStr.isEmpty()) { + continue; + } + sum += parseAndValidateNumber(numStr); + } + return sum; + } + + private int parseAndValidateNumber(String numStr) { + try { + int number = Integer.parseInt(numStr); + validateNumber(number); + return number; + } catch (NumberFormatException e) { + throw new RuntimeException("숫자 이외의 값이 포함되어 있습니다."); + } + } + + private void validateNumber(int number) { + if (number < 0) { + throw new RuntimeException("음수는 입력할 수 없습니다."); + } + } + + private static class DelimiterInfo { + final String delimiter; + final String textToParse; + + DelimiterInfo(String delimiter, String textToParse) { + this.delimiter = delimiter; + this.textToParse = textToParse; + } + } +} \ No newline at end of file diff --git a/src/test/java/CalcTest.java b/src/test/java/CalcTest.java new file mode 100644 index 00000000..f19a6662 --- /dev/null +++ b/src/test/java/CalcTest.java @@ -0,0 +1,60 @@ + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.*; + +@DisplayName("계산기 작동 테스트") +public class CalcTest { + private final Calc calc = new Calc(); + + @Test + @DisplayName("더하기 테스트") + void add() { + final var a = 1; + final var b = 2; + final var actual = calc.add(a, b); + + assertThat(actual).isEqualTo(3); + } + + @Test + @DisplayName("빼기 테스트") + void minus() { + final var a = 3; + final var b = 1; + final var actual = calc.minus(a, b); + + assertThat(actual).isEqualTo(2); + } + + @Test + @DisplayName("곱하기 테스트") + void times() { + final var a = 2; + final var b = 3; + final var actual = calc.times(a, b); + + assertThat(actual).isEqualTo(6); + } + + @Test + @DisplayName("나누기 테스트") + void divide() { + final var a = 10; + final var b = 5; + final var actual = calc.divide(a, b); + + assertThat(actual).isEqualTo(2); + } + + @Test + @DisplayName("0으로 나누기 예외 테스트") + void divideByZeroThrowsException() { + final var a = 10; + final var b = 0; + + assertThatThrownBy(() -> calc.divide(a, b)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("0 으로 나눌 수 없습니다."); + } +} \ No newline at end of file diff --git a/src/test/java/StringCalcTest.java b/src/test/java/StringCalcTest.java new file mode 100644 index 00000000..9cdb0195 --- /dev/null +++ b/src/test/java/StringCalcTest.java @@ -0,0 +1,92 @@ + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.*; + +@DisplayName("문자열 계산기 테스트") +class StringCalcTest { + private final StringCalc stringCalc = new StringCalc(); + + @Test + @DisplayName("쉼표 구분자 문자열 계산 테스트") + void addWithComma() { + final var input = "1,2,3"; + final var actual = stringCalc.add(input); + + assertThat(actual).isEqualTo(6); + } + + @Test + @DisplayName("콜론 구분자 문자열 계산 테스트") + void addWithColon() { + final var input = "1:2:3"; + final var actual = stringCalc.add(input); + + assertThat(actual).isEqualTo(6); + } + + @Test + @DisplayName("쉼표와 콜론 혼합 구분자 테스트") + void addWithMixedDelimiters() { + final var input = "1,2:3"; + final var actual = stringCalc.add(input); + + assertThat(actual).isEqualTo(6); + } + + @Test + @DisplayName("빈 문자열 테스트") + void addEmptyString() { + final var input = ""; + final var actual = stringCalc.add(input); + + assertThat(actual).isEqualTo(0); + } + + @Test + @DisplayName("null 입력 테스트") + void addNullInput() { + final String input = null; + final var actual = stringCalc.add(input); + + assertThat(actual).isEqualTo(0); + } + + @Test + @DisplayName("커스텀 구분자 테스트") + void addWithCustomDelimiter() { + final var input = "//;\n1;2;3"; + final var actual = stringCalc.add(input); + + assertThat(actual).isEqualTo(6); + } + + @Test + @DisplayName("음수 입력 예외 테스트") + void addNegativeNumberThrowsException() { + final var input = "1,-2,3"; + + assertThatThrownBy(() -> stringCalc.add(input)) + .isInstanceOf(RuntimeException.class) + .hasMessage("음수는 입력할 수 없습니다."); + } + + @Test + @DisplayName("숫자가 아닌 값 입력 예외 테스트") + void addNonNumericValueThrowsException() { + final var input = "1,a,3"; + + assertThatThrownBy(() -> stringCalc.add(input)) + .isInstanceOf(RuntimeException.class) + .hasMessage("숫자 이외의 값이 포함되어 있습니다."); + } + + @Test + @DisplayName("커스텀 구분자와 기본 구분자 혼합 테스트") + void addWithCustomAndDefaultDelimiters() { + final var input = "//@\n1@2,3:4"; + final var actual = stringCalc.add(input); + + assertThat(actual).isEqualTo(10); + } +} \ No newline at end of file