[nextstep_TDD_클린코드] 단위 테스트 실습 - 문자열 계산기

봄도둑·2023년 1월 20일
0

nextstep에서 진행하는 자바 플레이그라운드 with TDD, 클린코드 학습 내용입니다. 혼자 작성해본 연습 코드, 피드백 받은 내용을 정리합니다. 해당 코드를 더 나은 방향으로 개선할 수 있다면 댓글로 조언 꼭 부탁드립니다!

1. 테스트 코드

public class StringCalculatorTest {

    private void customizeSystemSetIn(String inputValue) {
        System.setIn(new ByteArrayInputStream(inputValue.getBytes()));
    }

    @ParameterizedTest
    @ValueSource(strings = {"입력한 문자열"})
    public void 문자열_입력_받기(String inputValue) {
        customizeSystemSetIn(inputValue);

        Scanner scanner = new Scanner(System.in);
        assertThat(scanner.nextLine()).isEqualTo("입력한 문자열");
    }

    @ParameterizedTest
    @ValueSource(strings = {"2 + 3 * 4 / 2"})
    public void 문자열_분리하기(String inputValue) {
        String[] splitInputValue = inputValue.split(" ");

        assertThat(splitInputValue).containsExactly("2", "+", "3", "*", "4", "/", "2");
    }

    @ParameterizedTest
    @ValueSource(strings = {"2 + 3 * 4 / 2"})
    public void 정수와_계산기호_분리_하기(String inputValue) {
        String[] splitInputValue = inputValue.split(" ");
        ArrayList<Integer> integerList = new ArrayList<>();
        ArrayList<String> mathematicalSignList = new ArrayList<>();

        for (String s : splitInputValue) {
            try {
                integerList.add(Integer.parseInt(s));
            } catch (NumberFormatException e) {
                mathematicalSignList.add(s);
            }
        }

        assertThat(integerList).containsExactly(2, 3, 4, 2);
        assertThat(mathematicalSignList).containsExactly("+", "*", "/");
    }

    @Test
    public void 정수_리스트와_계산_기호_리스트를_사용해_순차적_사칙연산_실행하기() {
        ArrayList<Integer> integerList = new ArrayList<>(Arrays.asList(2, 3, 4, 2));
        ArrayList<String> mathematicalSignList = new ArrayList<>(Arrays.asList("+", "*", "/"));
        int calcResult = 0;

        for (int i = 0; i < integerList.size(); i++) {
            if (i == 0) {
                calcResult = integerList.get(i);
                continue;
            }
            //메소드로 빼내고 싶은 부분
            calcResult = 사칙연산_기호를_입력받아_두_수의_사칙연산_수행하기(mathematicalSignList.get(i-1), calcResult, integerList.get(i));
        }

        assertThat(calcResult).isEqualTo(10);
    }

    //테스트 수행 완료 후 메소드 처리를 위해 일반 private 메소드로 변환
    private int 사칙연산_기호를_입력받아_두_수의_사칙연산_수행하기(String mathematicalSign, int calcResult, int pramInt) {
        if (mathematicalSign.equals("+")) {
            calcResult = calcResult + pramInt;
            return calcResult;
        }

        if (mathematicalSign.equals("-")) {
            calcResult = calcResult - pramInt;
            return calcResult;
        }

        if (mathematicalSign.equals("*")) {
            calcResult = calcResult * pramInt;
            return calcResult;
        }

        calcResult = calcResult / pramInt;
        return calcResult;
    }
}

2. 프로덕션 코드

  • StringCalculator.java
    - 아쉬운 부분(나 혼자 피드백)
    1. 입력한 값에 대한 validation 처리가 너무 빈약함.
    2. intList와 mathematicalSignList를 나눌 때 try, catch를 사용하지 않고 분배하는 방법을 사용했으면 함
    3. void 메소드를 사용하기 보다 return 값을 들고 있는 메소드로 개선 해보고 싶음 -> void의 경우 해당 메소드를 통한 동작이 성공했는지 실패했는지 정확하게 알기 어려운 문제가 있음
    4. 매직 넘버(0, "+", "*" etc...)등 final 변수로 빼놓기는 했는데 enum으로 처리하는 게 더 좋은 걸까???
    5. mathematicalSignList은 "+", "-" 등 연산 기호만 받는 일급 컬렉션으로 쓸 수 있을 것 같은데...
public class StringCalculator {
    private final String ADDITION_SIGN = "+";
    private final String SUBTRACTION_SIGN = "-";
    private final String MULTIPLICATION_SIGN = "*";
    private final String DIVISION_SIGN = "/";
    private final int INIT_INT = 0;

    private String inputValue;
    private ArrayList<Integer> intList = new ArrayList<>();
    private ArrayList<String> mathematicalSignList = new ArrayList<>();
    private int result;

    public StringCalculator() {
    }

    public StringCalculator(String inputValue) {
        this.inputValue = inputValue;
    }

    public int getResult() {

        return result;
    }

    public String getInputValue() {
        return inputValue;
    }

    public void setInputValue(String inputValue) {
        this.inputValue = inputValue;
    }

    public void calculate() {
        this.setList();
        this.result = this.calculateIntList();
    }

    private String[] splitInputValue() {
        return this.inputValue.split(" ");
    }

    private void setList() {
        for (String s : splitInputValue()) {
            try {
                this.intList.add(Integer.parseInt(s));
            } catch (NumberFormatException e) {
                this.mathematicalSignList.add(s);
            }
        }
    }

    private int calculateTwoInt(String mathematicalSign, int calcResult, int pramInt) {
        if (Objects.isNull(mathematicalSign) || mathematicalSign.equals("")) {
            return calcResult;
        }

        if (mathematicalSign.equals(ADDITION_SIGN)) {
            return calcResult + pramInt;
        }

        if (mathematicalSign.equals(SUBTRACTION_SIGN)) {
            return calcResult - pramInt;
        }

        if (mathematicalSign.equals(MULTIPLICATION_SIGN)) {
            return calcResult * pramInt;
        }

        return calcResult / pramInt;
    }

    private int calculateIntList() {
        int calcResult = INIT_INT;
        for (int i = INIT_INT; i < this.intList.size(); i++) {
            if (i == INIT_INT) {
                calcResult = this.intList.get(i);
                continue;
            }

            calcResult = calculateTwoInt(this.mathematicalSignList.get(i - 1), calcResult, this.intList.get(i));
        }

        return calcResult;
    }
}
  • calculate.java (main)
public class calculate {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        StringCalculator calculator = new StringCalculator(scanner.nextLine());

        calculator.calculate();
        System.out.println("result : " + calculator.getResult());
    }
}
profile
Java Spring 백엔드 개발자입니다. java 외에도 다양하고 흥미로운 언어와 프레임워크를 학습하는 것을 좋아합니다.

1개의 댓글

comment-user-thumbnail
2023년 6월 30일

와 객체지향적으로 엄청 잘짜셨네요 제가 짠거랑 비교되네요..ㅎ

답글 달기