{"id":15068876,"url":"https://github.com/kos5667/test-driven-development-with-java","last_synced_at":"2026-01-03T05:07:07.277Z","repository":{"id":232813282,"uuid":"646396345","full_name":"kos5667/test-driven-development-with-java","owner":"kos5667","description":"Java와 Spring으로 하는 TDD 연습 및 여러가지 케이스 정리","archived":false,"fork":false,"pushed_at":"2023-06-06T13:49:27.000Z","size":80,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-22T19:48:34.176Z","etag":null,"topics":["assertj","gradle","java11","junit-jupiter","junit4","junit5","spring-boot","tdd","tdd-java"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kos5667.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-28T09:07:43.000Z","updated_at":"2023-06-09T15:09:16.000Z","dependencies_parsed_at":null,"dependency_job_id":"2d45ff31-7fc0-4a27-9486-f4cb07a7f09d","html_url":"https://github.com/kos5667/test-driven-development-with-java","commit_stats":null,"previous_names":["kos5667/test-driven-development-with-java"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kos5667%2Ftest-driven-development-with-java","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kos5667%2Ftest-driven-development-with-java/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kos5667%2Ftest-driven-development-with-java/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kos5667%2Ftest-driven-development-with-java/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kos5667","download_url":"https://codeload.github.com/kos5667/test-driven-development-with-java/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243838136,"owners_count":20355985,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["assertj","gradle","java11","junit-jupiter","junit4","junit5","spring-boot","tdd","tdd-java"],"created_at":"2024-09-25T01:39:33.991Z","updated_at":"2026-01-03T05:07:07.226Z","avatar_url":"https://github.com/kos5667.png","language":"Java","readme":"# 🚀String 클래스에 대한 학습 테스트 - 1단계\n## 요구사항 1\n* \"1,2\"을 ,로 split 했을 때 1과 2로 잘 분리되는지 확인하는 학습 테스트를 구현한다.\n* \"1\"을 ,로 split 했을 때 1만을 포함하는 배열이 반환되는지에 대한 학습 테스트를 구현한다.\n\n## 힌트\n* 배열로 반환하는 값의 경우 assertj의 contains()를 활용해 반환 값이 맞는지 검증한다.\n* 배열로 반환하는 값의 경우 assertj의 containsExactly()를 활용해 반환 값이 맞는지 검증한다.\n\n## 요구사항 2\n* \"(1,2)\" 값이 주어졌을 때 String의 substring() 메소드를 활용해 ()을 제거하고 \"1,2\"를 반환하도록 구현한다.\n\n## 요구사항 3\n* \"abc\" 값이 주어졌을 때 String의 charAt() 메소드를 활용해 특정 위치의 문자를 가져오는 학습 테스트를 구현한다.\n* String의 charAt() 메소드를 활용해 특정 위치의 문자를 가져올 때 위치 값을 벗어나면 StringIndexOutOfBoundsException이 발생하는 부분에 대한 학습 테스트를 구현한다.\n* JUnit의 @DisplayName을 활용해 테스트 메소드의 의도를 드러낸다.\n\n* 힌트\n  AssertJ Exception Assertions 문서 참고\n\n* 자주 발생하는 Exception의 경우 Exception별 메서드 제공하고 있음\n* assertThatIllegalArgumentException()\n* assertThatIllegalStateException()\n* assertThatIOException()\n* assertThatNullPointerException()\n\n\n## Set Collection에 대한 학습 테스트\n* 다음과 같은 Set 데이터가 주어졌을 때 요구사항을 만족해야 한다.\n\n```java \npublic class SetTest {\n    private Set\u003cInteger\u003e numbers;\n    \n    @BeforeEach\n    void setUp() {\n        numbers = new HashSet\u003c\u003e();\n        numbers.add(1);\n        numbers.add(1);\n        numbers.add(2);\n        numbers.add(3);\n    }\n    \n    // Test Case 구현\n}\n```\n## 요구사항 1\n* Set의 size() 메소드를 활용해 Set의 크기를 확인하는 학습테스트를 구현한다.\n\n## 요구사항 2\n* Set의 contains() 메소드를 활용해 1, 2, 3의 값이 존재하는지를 확인하는 학습테스트를 구현하려한다.\n* 구현하고 보니 다음과 같이 중복 코드가 계속해서 발생한다.\n* JUnit의 ParameterizedTest를 활용해 중복 코드를 제거해 본다.\n\n```java\n    @Test\n    void contains() {\n        assertThat(numbers.contains(1)).isTrue();\n        assertThat(numbers.contains(2)).isTrue();\n        assertThat(numbers.contains(3)).isTrue();\n    }\n```\n\n## 힌트\n```java\n@ParameterizedTest\n@ValueSource(strings = {\"\", \"  \"})\nvoid isBlank_ShouldReturnTrueForNullOrBlankStrings(String input) {\n    assertThat(Strings.isBlank(input)).isTrue();\n}\n```\n\n## 요구사항 3\n* 요구사항 2는 contains 메소드 결과 값이 true인 경우만 테스트 가능하다. 입력 값에 따라 결과 값이 다른 경우에 대한 테스트도 가능하도록 구현한다.\n* 예를 들어 1, 2, 3 값은 contains 메소드 실행결과 true, 4, 5 값을 넣으면 false 가 반환되는 테스트를 하나의 Test Case로 구현한다.\n\n## 힌트\n* Guide to JUnit 5 Parameterized Tests 문서에서 @CsvSource를 활용한다.\n\n# 🚀2단계 - 문자열 덧셈 계산기\n## 2단계 실습 시작\n\n## 문자열 덧셈 계산기를 통한 TDD 실습\n# 기능 요구사항\n* 쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환 (예: “” =\u003e 0, \"1,2\" =\u003e 3, \"1,2,3\" =\u003e 6, “1,2:3” =\u003e 6)\n* 앞의 기본 구분자(쉼표, 콜론)외에 커스텀 구분자를 지정할 수 있다. 커스텀 구분자는 문자열 앞부분의 “//”와 “\\n” 사이에 위치하는 문자를 커스텀 구분자로 사용한다. 예를 들어 “//;\\n1;2;3”과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 6이 반환되어야 한다.\n* 문자열 계산기에 숫자 이외의 값 또는 음수를 전달하는 경우 RuntimeException 예외를 throw한다.\n\n\n# 프로그래밍 요구사항\n* 메소드가 너무 많은 일을 하지 않도록 분리하기 위해 노력해 본다.\n\n* 기능 요구사항 분리 및 힌트\n1. 빈 문자열 또는 null 값을 입력할 경우 0을 반환해야 한다.(예 : “” =\u003e 0, null =\u003e 0)\n   기능 요구사항 분리 및 힌트\n```java\n    if (text == null) {}\n            if (text.isEmpty()) {}\n    }\n```\n2. 숫자 하나를 문자열로 입력할 경우 해당 숫자를 반환한다.(예 : “1”)\n```java\n   int number = Integer.parseInt(text);\n```\n\n3. 숫자 두개를 컴마(,) 구분자로 입력할 경우 두 숫자의 합을 반환한다.(예 : “1,2”)\n```java\n   String[] numbers = text.split(\",\");\n// 앞 단계의 구분자가 없는 경우도 split()을 활용해 구현할 수 있는지 검토해 본다.\n```\n\n4. 구분자를 컴마(,) 이외에 콜론(:)을 사용할 수 있다. (예 : “1,2:3” =\u003e 6)\n```java\nString[] tokens= text.split(\",|:\");\n```\n\n5. “//”와 “\\n” 문자 사이에 커스텀 구분자를 지정할 수 있다. (예 : “//;\\n1;2;3” =\u003e 6)\n```java\n   // java.util.regex 패키지의 Matcher, Pattern import\n   Matcher m = Pattern.compile(\"//(.)\\n(.*)\").matcher(text);\n   if (m.find()) {\n   String customDelimiter = m.group(1);\n   String[] tokens= m.group(2).split(customDelimiter);\n   // 덧셈 구현\n   }\n```\n\n6. 음수를 전달할 경우 RuntimeException 예외가 발생해야 한다. (예 : “-1,2,3”)\n* 구글에서 “junit4 expected exception”으로 검색해 해결책을 찾는다.\n\n\n## TestCase 소스 코드\n```java\nimport org.junit.jupiter.api.Test;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\n\npublic class StringAddCalculatorTest {\n@Test\npublic void splitAndSum_null_또는_빈문자() {\nint result = StringAddCalculator.splitAndSum(null);\nassertThat(result).isEqualTo(0);\n\n        result = StringAddCalculator.splitAndSum(\"\");\n        assertThat(result).isEqualTo(0);\n    }\n\n     @Test\n    public void splitAndSum_숫자하나() throws Exception {\n        int result = StringAddCalculator.splitAndSum(\"1\");\n        assertThat(result).isEqualTo(1);\n    }\n\n    @Test\n    public void splitAndSum_쉼표구분자() throws Exception {\n        int result = StringAddCalculator.splitAndSum(\"1,2\");\n        assertThat(result).isEqualTo(3);\n    }\n\n    @Test\n    public void splitAndSum_쉼표_또는_콜론_구분자() throws Exception {\n        int result = StringAddCalculator.splitAndSum(\"1,2:3\");\n        assertThat(result).isEqualTo(6);\n    }\n\n    @Test\n    public void splitAndSum_custom_구분자() throws Exception {\n        int result = StringAddCalculator.splitAndSum(\"//;\\n1;2;3\");\n        assertThat(result).isEqualTo(6);\n    }\n\n    @Test\n    public void splitAndSum_negative() throws Exception {\n        assertThatThrownBy(() -\u003e StringAddCalculator.splitAndSum(\"-1,2,3\"))\n                .isInstanceOf(RuntimeException.class);\n    }\n}\n```\n\nAssertJ Exception Assertions\n\n\n# 🚀3단계 - 자동차 경주\n## 기능 요구사항\n* 초간단 자동차 경주 게임을 구현한다.\n* 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.\n* 사용자는 몇 대의 자동차로 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.\n* 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4이상일 경우이다.\n* 자동차의 상태를 화면에 출력한다. 어느 시점에 출력할 것인지에 대한 제약은 없다.\n### 실행 결과\n* 위 요구사항에 따라 3대의 자동차가 5번 움직였을 경우 프로그램을 실행한 결과는 다음과 같다.\n```java\n자동차 대수는 몇 대 인가요?\n3\n시도할 회수는 몇 회 인가요?\n5\n\n실행 결과\n-\n-\n-\n\n--\n-\n--\n\n---\n--\n---\n\n----\n---\n----\n\n----\n----\n-----\n```\n\n### 힌트\n* 값을 입력 받는 API는 Scanner를 이용한다.\n\n* 랜덤 값은 자바 java.util.Random 클래스의 nextInt(10) 메소드를 활용한다.\n\n## 프로그래밍 요구사항\n* 모든 로직에 단위 테스트를 구현한다. 단, UI(System.out, System.in) 로직은 제외\n    * 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다.\n    * UI 로직을 InputView, ResultView와 같은 클래스를 추가해 분리한다.\n* 자바 코드 컨벤션을 지키면서 프로그래밍한다.\n    * 이 과정의 Code Style은 intellij idea Code Style. Java을 따른다.\n    * intellij idea Code Style. Java을 따르려면 code formatting 단축키(Windows : Ctrl + Alt + L. Mac : ⌥ (Option) + ⌘ (Command) + L.)를 사용한다.\n* else 예약어를 쓰지 않는다.\n    * 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다.\n    * else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.\n\n# 🚀4단계 - 자동차 경주(우승자)\n## 기능 요구사항\n* 각 자동차에 이름을 부여할 수 있다. 자동차 이름은 5자를 초과할 수 없다.\n* 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.\n* 자동차 이름은 쉼표(,)를 기준으로 구분한다.\n* 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한명 이상일 수 있다.\n### 실행 결과\n* 위 요구사항에 따라 3대의 자동차가 5번 움직였을 경우 프로그램을 실행한 결과는 다음과 같다.\n```java\n경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분).\npobi,crong,honux\n시도할 회수는 몇회인가요?\n5\n\n실행 결과\npobi : -\ncrong : -\nhonux : -\n\npobi : --\ncrong : -\nhonux : --\n\npobi : ---\ncrong : --\nhonux : ---\n\npobi : ----\ncrong : ---\nhonux : ----\n\npobi : -----\ncrong : ----\nhonux : -----\n\npobi : -----\ncrong : ----\nhonux : -----\n\npobi, honux가 최종 우승했습니다.\n```\n\n### 힌트\n* 규칙 1: 한 메서드에 오직 한 단계의 들여쓰기(indent)만 한다.\n    * indent가 2이상인 메소드의 경우 메소드를 분리하면 indent를 줄일 수 있다.\n    * else를 사용하지 않아 indent를 줄일 수 있다.\n* 자동차 이름을 쉼표(,)를 기준으로 분리하려면 String 클래스의 split(\",\") 메소드를 활용한다.\n```java\nString[] names = inputName.split(\",\");\n```\n* 사용자가 입력한 이름의 숫자 만큼 자동차 대수를 생성한다.\n* 자동차는 자동차 이름과 위치 정보를 가지는 Car 객체를 추가해 구현한다.\n\n## 프로그래밍 요구사항\n* indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다.\n    * 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.\n    * 힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메소드)를 분리하면 된다.\n* 함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다.\n    * 함수(또는 메소드)가 한 가지 일만 잘 하도록 구현한다.\n* 모든 로직에 단위 테스트를 구현한다. 단, UI(System.out, System.in) 로직은 제외\n    * 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다.\n    * UI 로직을 InputView, ResultView와 같은 클래스를 추가해 분리한다.\n* 자바 코드 컨벤션을 지키면서 프로그래밍한다.\n    * 참고문서: https://google.github.io/styleguide/javaguide.html 또는 https://myeonguni.tistory.com/1596\n* else 예약어를 쓰지 않는다.\n    * 힌트: if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다.\n    * else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.\n\n## 기능 목록 및 commit 로그 요구사항\n* 기능을 구현하기 전에 README.md 파일에 구현할 기능 목록을 정리해 추가한다.\n* git의 commit 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위로 추가한다.\n\n\n# 🚀 5단계 - 자동차 경주(리팩토링)\n## 리팩토링 요구사항\n* 핵심 비지니스 로직을 가지는 객체를 domain 패키지, UI 관련한 객체를 view 패키지에 구현한다.\n* MVC 패턴 기반으로 리팩토링해 view 패키지의 객체가 domain 패키지 객체에 의존할 수 있지만, domain 패키지의 객체는 view 패키지 객체에 의존하지 않도록 구현한다.\n  ![MVC 이미지][https://oopy.lazyrockets.com/api/v2/notion/image?src=https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F0bae1821-b9b6-4243-ace1-0e36f6d59606%2FUntitled.png\u0026blockId=52a35b94-5803-40d4-a11a-28dfbed1c307]\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkos5667%2Ftest-driven-development-with-java","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkos5667%2Ftest-driven-development-with-java","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkos5667%2Ftest-driven-development-with-java/lists"}