Skip to content

Commit

Permalink
[라빈] 온보딩 - 학습 테스트, 단위 테스트 (#3)
Browse files Browse the repository at this point in the history
* add : 요구사항 1 - split 했을 때 contains()를 활용해 변환 값이 맞는지 검증하는 테스트 코드 작성

* add : 요구사항 2 - substring()을 활용해 올바른 문자열이 반환되는지 테스트

* add : 요구사항 3 - charAt() 메소드를 활용해 범위를 넘어가는 예외 테스트

* add : 요구사항 1 - size() 메소드를 활용해 Set의 크기를 확인하는 테스트

* add : 요구사항 2 - JUnit의 ParameterizedTest를 이용해 중복 코드를 제거

* add : 요구사항 3 - @CsvSource를 활용해 입력 값에 따라 결과 값이 다른 경우의 테스트

* add : 문자열 계산기 기능 구현 및 테스트 코드 작성

* add : README 수정

* Update README.md

* refact : operator를 enum 클래스로 만들어서 관리 / Validator -> ValidityInspector 이름 변경

* refact : if 문의 나열 -> if ~ else if 문으로 변경 / 조건문의 단계별로 return

* refact : 입력 문자열의 공백 또는 null 검사 메소드를 하나로 통일 / 매직넘버 제거 / checkIsDoubleNumber -> checkCorrectDoubleNumber, checkIsOperator -> checkCorrectOperator 로 메소드 이름 변경

* modify : gitignore 수정

* refact : call stack 이 쌓이는 것을 방지하기 위해 잘못된 문자열이 입력될 시 계산기의 기능이 종료되도록 변경 / Validator 에서 is 로 시작하는 메소드 명 변경 / 공백으로만 이루어진 문자열 입력 시 예외 출력 / Input 클래스의 기능을 명확히 설명할 수 있도록 클래스 이름과 메소드 이름을 변경

* refact : 여러 클래스에서 공통적으로 사용하는 0, 공백, 빈 문자열을 상수 클래스로 분리

* refact : 변경한 메소드에 맞게 테스트 코드 수정 / MethodSource -> ValueSource 로 변경

* refact : Operator 를 컨벤션에 맞게 이름 수정 / Operator.find() 와 Operator.calculate() 메소드를 만들어 입력한 연산자와 좌항, 우항을 계산하게 수정
  • Loading branch information
giantim authored Feb 10, 2020
1 parent 8256e24 commit 6fe685c
Show file tree
Hide file tree
Showing 13 changed files with 352 additions and 2 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
!gradle/wrapper/gradle-wrapper.jar
/out/
/target/
/src/main/java/nickname.txt
/src/main/java/study
/src/test/java/study/CabenetTest.java


### STS ###
.apt_generated
Expand Down
35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
# java-calculator
문자열 계산기 미션 저장소

## 우아한테크코스 코드리뷰
* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)
## 기능적 요구사항
- 문자열을 입력 받아 공백 단위로 split 하는 함수를 만든다
- 입력이 정상적인지 확인
- 입력이 없는지
- 숫자와 연산자 외의 다른 문자가 있는지
- split 된 결과가 정상적인지 확인
- 숫자와 연산자의 순서가 맞는지

- 사칙연산을 하는 함수를 만든다
- 나눗셈의 경우 0으로 나누는 경우에 대한 예외 처리
- 입력 값이 자료형의 범위를 넘어 가는 경우에 대한 예외 처리

- split 된 결과 값이 순서대로 연산이 되는 함수를 만든다

- 연산 결과를 출력하는 함수를 만든다

- 단위별로 테스트 하는 함수를 만든다
- split 테스트를 진행한다
- 입력으로 null, 공백, 유효하지 않은 double 범위의 수, 숫자와 연산자 외의 문자열
- split 할 수 없는 문자열, 숫자와 연산자 순서가 바뀐 문자열, 정상 계산식
- 사칙연산 테스트
- 사칙연산의 결과 값이 실제 값과 맞는지 테스트
- 0으로 나누었을 때, double 범위를 넘어가는 결과 값


## 비기능적 요구사항
- indent 2까지 허용한다.
- 메소드는 15줄 까지 허용한다.
- switch문 사용하지 않는다.
- 하드코딩하지 않는다.
- 변수와 메소드 명명은 컨벤션에 따른다.
- 자바 코드 컨벤션을 지키면서 프로그래밍 한다.
- 함수의 인자 수를 3개까지만 허용한다.
13 changes: 13 additions & 0 deletions src/main/java/calculator/Calculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package calculator;

public class Calculator {
public double calculate(String[] splitedInput) {
double left = Double.valueOf(splitedInput[0]);
for (int i = 1; i < splitedInput.length; i = i + 2) {
String operator = splitedInput[i];
double right = Double.valueOf(splitedInput[i + 1]);
left = Operator.find(operator).calculate(left, right);
}
return left;
}
}
8 changes: 8 additions & 0 deletions src/main/java/calculator/Constant.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package calculator;

public class Constant {
public final static int ZERO = 0;

public final static String BLANK = " ";
public final static String EMPTY_STRING = "";
}
17 changes: 17 additions & 0 deletions src/main/java/calculator/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package calculator;

public class Main {
public static void main(String[] args) {
UserInputScanner userInputScanner = new UserInputScanner();
Calculator calculator = new Calculator();
Output output = new Output();

try {
String[] splitString = userInputScanner.splitUserInputString();
double result = calculator.calculate(splitString);
output.printResult(result);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
47 changes: 47 additions & 0 deletions src/main/java/calculator/Operator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package calculator;

import java.util.Arrays;
import java.util.function.BiFunction;

public enum Operator {
PLUS("+",
(left, right) -> (left + right)),
MINUS("-",
(left, right) -> (left - right)),
MULTIPLICATION("*",
(left, right) -> (left * right)),
DIVISION("/",
(left, right) -> (left / right));

private String symbol;
private BiFunction<Double, Double, Double> expression;

Operator(String symbol, BiFunction<Double, Double, Double> expression) {
this.symbol = symbol;
this.expression = expression;
}

private String getSymbol() {
return this.symbol;
}

public static Operator find(String operator) {
return Arrays.stream(Operator.values())
.filter(op -> op.getSymbol().equals(operator))
.findFirst()
.get();
}

public double calculate(double left, double right) {
return expression.apply(left, right);
}

public static boolean isRightOperator(String operator) {
return Arrays.stream(Operator.values())
.anyMatch(op -> op.getSymbol().equals(operator));
}

public static boolean isDivisionOperator(String operator) {
return "/".equals(operator);
}
}
7 changes: 7 additions & 0 deletions src/main/java/calculator/Output.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package calculator;

public class Output {
public void printResult(double result) {
System.out.println("결과 값은 : " + result);
}
}
22 changes: 22 additions & 0 deletions src/main/java/calculator/UserInputScanner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package calculator;

import java.util.Scanner;

public class UserInputScanner {
private ValidityInspector validityInspector = new ValidityInspector();
private Scanner scanner = new Scanner(System.in);

private String inputStringToUser() {
System.out.print("계산식을 입력하시오: ");
String inputLine = scanner.nextLine();
validityInspector.checkUserInputIsBlankOrEmpty(inputLine);
return inputLine;
}

public String[] splitUserInputString() {
String input = inputStringToUser();
String[] splitInput = input.split(Constant.BLANK);
validityInspector.checkCanConvertUserInputToNumberAndOperator(splitInput);
return splitInput;
}
}
60 changes: 60 additions & 0 deletions src/main/java/calculator/ValidityInspector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package calculator;

public class ValidityInspector {
public void checkUserInputIsBlankOrEmpty(String input) {
if (isBlank(input) || isEmpty(input)) {
throw new IllegalArgumentException("공백 또는 빈 문자열을 입력하셨습니다.");
}
}

private boolean isEmpty(String input) {
return input.equals(Constant.EMPTY_STRING);
}

private boolean isBlank(String input) {
return input.equals(Constant.BLANK);
}

public void checkCanConvertUserInputToNumberAndOperator(String[] splitedInput) {
checkSplitedInputEmpty(splitedInput);
String firstClause = splitedInput[0];
checkCorrectDoubleNumber(firstClause);
try {
for (int i = 1; i < splitedInput.length; i = i + 2) {
String operator = splitedInput[i];
String secondClause = splitedInput[i + 1];
checkDivideByZero(operator, secondClause);
checkCorrectOperator(operator);
checkCorrectDoubleNumber(secondClause);
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new ArrayIndexOutOfBoundsException("잘못된 계산식을 입력하였습니다.");
}
}

private void checkCorrectDoubleNumber(String s) {
try {
Double.valueOf(s);
} catch (Exception e) {
throw new IllegalArgumentException("잘못된 수를 입력하였습니다.");
}
}

private void checkCorrectOperator(String s) {
if (!Operator.isRightOperator(s)) {
throw new IllegalArgumentException("잘못된 연산자를 입력하였습니다.");
}
}

private void checkSplitedInputEmpty(String[] splitedInput) {
if (splitedInput.length == 0) {
throw new IllegalArgumentException("공백을 입력하였습니다.");
}
}

private void checkDivideByZero(String operator, String secondClause) {
if (Operator.isDivisionOperator(operator) && Integer.toString(Constant.ZERO).equals(secondClause)) {
throw new IllegalArgumentException("0으로 나누는 식을 입력하셨습니다.");
}
}
}
64 changes: 64 additions & 0 deletions src/test/java/calculator/CalculatorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package calculator;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

public class CalculatorTest {
private Calculator calculator;
private ValidityInspector validityInspector;

@BeforeEach
public void setUp() {
calculator = new Calculator();
validityInspector = new ValidityInspector();
}

@ParameterizedTest
@ValueSource(strings = {" ", ""})
public void checkUserInputIsBlankOrEmptyTest(String input) {
Assertions.assertThatThrownBy(() -> {
validityInspector.checkUserInputIsBlankOrEmpty(input);
}).isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("공백 또는 빈 문자열을 입력하셨습니다.");
}

@ParameterizedTest
@ValueSource(strings = {" ", "q + w + e", "1+2+3", "+ + 2", "1 abcd 2", "2 * 65 / 0", "2 + 3/", "/1 + 55", "1 + a3"})
public void checkCanConvertUserInputToNumberAndOperatorTest(String input) {
String[] splitData = input.split(Constant.BLANK);
Assertions.assertThatThrownBy(() -> {
validityInspector.checkCanConvertUserInputToNumberAndOperator(splitData);
}).isInstanceOf(IllegalArgumentException.class);
}

@Test
public void addTest() {
String[] numericalExpression = {"1", "+", "2"};
Double result = calculator.calculate(numericalExpression);
Assertions.assertThat(result).isEqualTo(3);
}

@Test
public void subtractTest() {
String[] numericalExpression = {"1", "-", "2"};
Double result = calculator.calculate(numericalExpression);
Assertions.assertThat(result).isEqualTo(-1);
}

@Test
public void multipleTest() {
String[] numericalExpression = {"1", "*", "2"};
Double result = calculator.calculate(numericalExpression);
Assertions.assertThat(result).isEqualTo(2);
}

@Test
public void divideTest() {
String[] numericalExpression = {"1", "/", "2"};
Double result = calculator.calculate(numericalExpression);
Assertions.assertThat(result).isEqualTo(0.5d);
}
}
Empty file removed src/test/java/empty.txt
Empty file.
44 changes: 44 additions & 0 deletions src/test/java/study/SetTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package study;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;

import java.util.HashSet;
import java.util.Set;

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

public class SetTest {
private Set numbers;

@BeforeEach
void setUp() {
numbers = new HashSet<>();
numbers.add(1);
numbers.add(1);
numbers.add(2);
numbers.add(3);
}

@Test
void sizeTest() {
int size = numbers.size();
Assertions.assertThat(size).isEqualTo(4);
}

@ParameterizedTest
@ValueSource(ints = {1, 1, 2, 3})
void containTest(int number) {
assertTrue(numbers.contains(number));
}

@ParameterizedTest
@CsvSource(value = {"ravie-RAVIE", "Lavine-LAVINE", "orange-ORANKE", "dog-DoG"}, delimiter = '-')
void equalUpperCase(String input, String upperCase){
Assertions.assertThat(upperCase).isEqualTo(input.toUpperCase());
}
}
33 changes: 33 additions & 0 deletions src/test/java/study/StringTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package study;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.assertj.core.api.Assertions;

public class StringTest {
@Test
void split() {
String value = "1,2";
String[] result = value.split(",");
Assertions.assertThat(result).contains("1");
Assertions.assertThat(result).contains("2");
String[] temp = {"1", "2"};
Assertions.assertThat(result).containsExactly(temp);
}

@Test
void substring() {
String value = "(1,2)";
String result = value.substring(1, value.length() - 1);
Assertions.assertThat(result).contains("1,2");
}

@Test
@DisplayName("charAt method out of bounds test")
void charAtTest() {
String value = "abc";
Assertions.assertThatThrownBy(() -> {
value.charAt(10);
}).isInstanceOf(StringIndexOutOfBoundsException.class).hasMessageContaining("String index out of range");
}
}

0 comments on commit 6fe685c

Please sign in to comment.