Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LBP] 송하은 미션 3,4단계 제출합니다. #72

Open
wants to merge 6 commits into
base: haeunsong
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ repositories {

dependencies {
testImplementation platform('org.junit:junit-bom:5.9.1')
testImplementation platform('org.assertj:assertj-bom:3.25.1')
testImplementation('org.junit.jupiter:junit-jupiter')
testImplementation('org.assertj:assertj-core')
}

test {
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/Calculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
public class Calculator {

public int add(int a, int b) {
return a + b;
}

public int subtract(int a, int b) {
return a - b;
}

public int multiply(int a, int b) {
return a * b;
}

public int divide(int a, int b) {
if (b == 0) {
throw new ArithmeticException("Divide by zero");
}
return a / b;
}
}
91 changes: 91 additions & 0 deletions src/main/java/StringCalculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import java.util.regex.Pattern;

public class StringCalculator {

/*
1. 입력값 검증
2. 구분자 추출
3. 문자열을 구분자 기준으로 분리
4. 분리된 숫자 문자열 배열을 합산
*/

public int splitStrBySeparaterAndGetSum(String str) {
// 1. 입력값 검증
if(str.length() == 0) {
return 0;
}

// 2. 구분자, 커스텀 구분자일 경우 구분자 제외 검사해야할 문자열 추출
String separater = extractSeparater(str);
String numbers = extractNumbers(str);

// 3. 문자열을 구분자 기준으로 분리
String [] tokens = numbers.split(separater);
validateTokens(tokens);

// 4. 분리된 숫자 문자열 배열 합산
return calculateSum(tokens);
}

private String extractSeparater(String str) {
// 기본 구분자는 , 또는 : 이다.
String separater = "[,|:]";

// 커스텀 구분자가 존재하는 경우
if (str.startsWith("//") && str.contains("\n")) {
int newLineIdx = str.indexOf("\n");
// /n 을 찾을 수 없는 경우
if (newLineIdx == -1) {
throw new RuntimeException("커스텀 구분자를 '//' 와 '\\n' 문자 사이에 정확하게 입력해주세요.");
}
separater = Pattern.quote(str.substring(2, newLineIdx));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정규식 특수문자를 안전하게 처리하셨네요! 👍

}
return separater;
}

private String extractNumbers(String str) {
String numbers = "";

// 커스텀 구분자일 경우
if(str.startsWith("//")) {
numbers = str.substring(str.indexOf("\n") + 1);
return numbers;
}

// 기본 구분자이면 그대로 str 반환
return str;
}

private void validateTokens(String[] tokens) {
for(String token : tokens) {
if (token.isEmpty()) {
throw new RuntimeException("잘못된 입력 형식입니다.");
}

if(parseNumber(token) < 0) {
throw new RuntimeException("음수는 사용할 수 없습니다.");
}
}
}

private int calculateSum(String[] tokens) {
int sum = 0;
for(String token : tokens) {
sum += Integer.parseInt(token);
}
return sum;
}

private int parseNumber(String number) {
// 숫자가 아니라면 예외 발생
try {
return Integer.parseInt(number);
} catch (NumberFormatException e) {
throw new RuntimeException("숫자가 아닌 값이 포함되어 있습니다.");
}
}

private boolean isNumeric(String str) {
return str.matches("-?\\d+"); // 정수 여부를 확인하는 정규식 (-는 음수 허용)
}
}
48 changes: 48 additions & 0 deletions src/test/java/CalculatorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

@DisplayName("초간단 계산기 테스트")
class CalculatorTest {
private final Calculator calculator = new Calculator();

@Nested
class Add {
@Test
void add() {
assertEquals(5, calculator.add(2, 3));
}
}

@Nested
class Subtract {
@Test
void subtract() {
assertEquals(-3, calculator.subtract(3, 6));
}
}

@Nested
class Multiply {
@Test
void multiply() {
assertEquals(6, calculator.multiply(2, 3));
}
}

@Nested
class Divide{
@Test
@DisplayName("0으로 나누면 ArithmeticException 예외가 발생한다.")
void should_throw_exception_when_divide_by_zero() {
assertThrows(ArithmeticException.class, () -> calculator.divide(3,0));
}

@Test
void divideByNotZero() {
assertEquals(2, calculator.divide(6, 3));
}
}
}
83 changes: 83 additions & 0 deletions src/test/java/StringCalculatorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

@DisplayName("문자열 계산기 테스트")
class StringCalculatorTest {

private StringCalculator stringCalculator = new StringCalculator();

@Nested
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nested를 통해서 테스트를 그룹화 하셨네요 좋습니다!

@DisplayName("기본 구분자 테스트")
class basicSeparaterTest {

@ParameterizedTest(name = "입력값 \"{0}\"은 결과값 {1}을 반환해야 한다.")
@CsvSource({
"'', 0",
"'123', 123",
"'1,2:3', 6"
})
void should_return_correct_sum(String input, int expected) {
assertThat(stringCalculator.splitStrBySeparaterAndGetSum(input)).isEqualTo(expected);
}
}

@Nested
@DisplayName("커스텀 구분자 테스트")
class customSeparaterTest {
@Test
@DisplayName("커스텀 구분자로 분리한 숫자들의 합을 반환해야 한다")
void should_sum_numbers_separated_by_custom_separator() {
assertThat(6).isEqualTo(stringCalculator.splitStrBySeparaterAndGetSum("//;\n1;2;3"));
assertThat(2).isEqualTo(stringCalculator.splitStrBySeparaterAndGetSum("//;\n2"));
}
}

@Nested
@DisplayName("Runtime Exception 테스트")
class runtimeExceptionTest {
@Test
@DisplayName("숫자가 아닌 값이 포함되면 예외를 발생해야 한다.")
void should_throw_exception_when_contains_non_number() {

// 특수문자가 포함된 경우
assertThatThrownBy(() -> stringCalculator.splitStrBySeparaterAndGetSum("//;\n1;*;3"))
.isInstanceOf(RuntimeException.class)
.hasMessageContaining("숫자가 아닌 값이 포함되어 있습니다.");

assertThatThrownBy(() -> stringCalculator.splitStrBySeparaterAndGetSum("//;\n1456;-;3"))
.isInstanceOf(RuntimeException.class)
.hasMessageContaining("숫자가 아닌 값이 포함되어 있습니다.");

// 글자가 포함된 경우
assertThatThrownBy(() -> stringCalculator.splitStrBySeparaterAndGetSum("//;\nddd;ad;3"))
.isInstanceOf(RuntimeException.class)
.hasMessageContaining("숫자가 아닌 값이 포함되어 있습니다.");
}

@Test
@DisplayName("음수가 포함되면 예외를 발생해야 한다.")
void should_throw_exception_when_contains_minus() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다른 메서드들과 컨벤션이 맞춰지지 않은 것 같아요
메소드 시그니처 라인과의 개행을 하은이만의 기준으로 통일해봅시다!


assertThatThrownBy(() -> stringCalculator.splitStrBySeparaterAndGetSum("-4,2:-9"))
.isInstanceOf(RuntimeException.class)
.hasMessageContaining("음수는 사용할 수 없습니다.");
}

@Test
@DisplayName("또 다른 잘못된 입력 형식의 경우 예외를 발생해야 한다.")
void should_throw_exception_when_another_wrong_input() {

assertThatThrownBy(() -> stringCalculator.splitStrBySeparaterAndGetSum("//;1;2;\n;"))
.isInstanceOf(RuntimeException.class)
.hasMessageContaining("잘못된 입력 형식입니다.");

}
}

Comment on lines +79 to +82
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

불필요한 개행들이 보입니다 제거해볼까요?

}