diff --git a/README.md b/README.md index ff2aca185e..3029f06f78 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ 본 저장소는 TDD 학습을 위한 단계별 과제를 포함합니다. ## 단계별 문서 - **[1단계 - 스트림, 람다, Optional](./docs/01-stream-lambda-optional.md)** -- **[2단계 - 사다리(생성)]()** +- **[2단계 - 사다리(생성)](./docs/02-ladder-creation.md)** - **[3단계 - 사다리(게임 실행)]()** - **[4단계 - 사다리(리팩터링)]()** diff --git a/docs/02-ladder-creation.md b/docs/02-ladder-creation.md new file mode 100644 index 0000000000..c8dd41c03f --- /dev/null +++ b/docs/02-ladder-creation.md @@ -0,0 +1,107 @@ +# 2단계 - 사다리(생성) +*** +## 코드 리뷰 +> PR 링크: +> **[https://github.com/next-step/java-ladder/pull/2431](https://github.com/next-step/java-ladder/pull/2431)** + +## 기능 요구사항 +- 사다리 게임에 참여하는 사람에 이름을 최대5글자까지 부여할 수 있다. 사다리를 출력할 때 사람 이름도 같이 출력한다. +- 사람 이름은 쉼표(,)를 기준으로 구분한다. +- 사람 이름을 5자 기준으로 출력하기 때문에 사다리 폭도 넓어져야 한다. +- 사다리 타기가 정상적으로 동작하려면 라인이 겹치지 않도록 해야 한다. +- |-----|-----| 모양과 같이 가로 라인이 겹치는 경우 어느 방향으로 이동할지 결정할 수 없다. + +## 프로그래밍 요구사항 +- **자바 8의 스트림과 람다를 적용해 프로그래밍한다.** +- **규칙 6: 모든 엔티티를 작게 유지한다.** + +### 실행 결과 +- 위 요구사항에 따라 4명의 사람을 위한 5개 높이 사다리를 만들 경우, 프로그램을 실행한 결과는 다음과 같다. +```text +참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요) +pobi,honux,crong,jk + +최대 사다리 높이는 몇 개인가요? +5 + +실행결과 + +pobi honux crong jk + |-----| |-----| + | |-----| | + |-----| | | + | |-----| | + |-----| |-----| +``` + +### 힌트 +- 2차원 배열을 ArrayList, Generic을 적용해 구현하면 ArrayList>와 같이 이해하기 어려운 코드가 추가된다. +- 사다리 게임에서 한 라인의 좌표 값을 가지는 객체를 추가해 구현해 본다. +```java +public class Line { + private List points = new ArrayList<>(); + + public Line (int countOfPerson) { + // 라인의 좌표 값에 선이 있는지 유무를 판단하는 로직 추가 + } + + [...] +} +``` +- 위와 같이 Line 객체를 추가하면 ArrayList> 코드를 ArrayList과 같이 구현하는 것이 가능해 진다. + +## PR 전 점검 +**[체크리스트 확인하기](checklist.md)** + +## 구현 기능 목록 + +### 참가자 이름 +- [x] 이름 (Name) + - [x] 문자열로 생성 + - [x] null 또는 빈 값일 시 예외 발생 + - [x] 5자 초과 시 예외 발생 + - [x] 6자 고정 출력용 문자열 반환 + +- [x] 이름 목록 (Names) + - [x] 쉼표 구분 문자열로 생성 + - [x] 이름 목록으로 생성 + - [x] null일 시 예외 발생 + - [x] 2명 미만일 시 예외 발생 + - [x] 참가자 수 반환 + - [x] 출력용 문자열 반환 + +### 사다리 높이 +- [x] 높이 (Height) + - [x] 정수로 생성 + - [x] 1 미만일 시 예외 발생 + +### 사다리 라인 +- [x] 라인 (Line) + - [x] Boolean 목록으로 생성 + - [x] 가변인자로 생성 + - [x] null 또는 빈 값일 시 예외 발생 + - [x] 연속된 true 존재 시 예외 발생 + - [x] 출력용 문자열 반환 + +- [x] 라인 목록 (Lines) + - [x] Line 목록으로 생성 + - [x] Height, 참가자 수, LineGenerator로 생성 + - [x] 출력용 문자열 반환 + +### 사다리 생성 +- [x] 라인 생성기 인터페이스 (LineGenerator) + - [x] Line 생성 메서드 정의 + +- [x] Boolean 생성기 인터페이스 (BooleanGenerator) + - [x] boolean 생성 메서드 정의 + +- [x] 랜덤 라인 생성기 (RandomLineGenerator) + - [x] LineGenerator 구현 + - [x] BooleanGenerator 주입 가능 + - [x] 기본 생성 시 Random 사용 + - [x] 연속 true 방지 로직 적용 + +### 사다리 +- [x] 사다리 (Ladder) + - [x] Names, Lines로 생성 + - [x] 출력용 문자열 반환 diff --git a/docs/checklist.md b/docs/checklist.md new file mode 100644 index 0000000000..745c30ea65 --- /dev/null +++ b/docs/checklist.md @@ -0,0 +1,31 @@ +# 미션 제출 전 체크리스트 + +### 1. 객체지향 생활체조 원칙 +- 규칙 1: 한 메서드에 오직 한 단계의 들여쓰기(indent)만 사용 +- 규칙 2: else 예약어 사용 금지 +- 규칙 3: 모든 원시값과 문자열을 포장 (Primitive Obsession 제거) +- 규칙 4: 한 줄에 점(.) 하나만 사용 (Law of Demeter) +- 규칙 5: 축약 금지 (명확한 네이밍) +- 규칙 6: 모든 엔티티 작게 유지 +- 규칙 7: 3개 이상의 인스턴스 변수를 가진 클래스 지양 +- 규칙 8: 일급 콜렉션 사용 +- 규칙 9: Getter/Setter 사용 지양 (Tell, Don't Ask) + +### 2. 메서드 작성 원칙 +- 한 가지 일만 수행하는 작은 메서드 (최대 10라인) + +### 2. 객체지향 설계 품질 +- 가장 작은 단위의 객체부터 시작 (상향식 개발) +- 객체에게 상태 노출 대신 책임 위임 +- 불변성을 최대한 유지 +- 도메인 용어로 네이밍 +- 협력 구조가 자연스럽게 읽힘 + +### 3. TDD +- TDD 사이클(Red-Green-Refactor)로 구현 +- TDD 사이클 단위 커밋 + +### 4. 테스트 품질 +- 객체의 책임과 결과만 검증 (구현 세부사항 테스트 지양) +- 단순 위임은 테스트 생략, 조건·분기·조합이 있다면 테스트 +- 테스트 객체 생성 헬퍼메서드가 반복되면 부생성자 도입 검토 diff --git a/src/main/java/nextstep/ladder/Application.java b/src/main/java/nextstep/ladder/Application.java new file mode 100644 index 0000000000..ee038ebf49 --- /dev/null +++ b/src/main/java/nextstep/ladder/Application.java @@ -0,0 +1,17 @@ +package nextstep.ladder; + +import nextstep.ladder.domain.*; +import nextstep.ladder.view.InputView; +import nextstep.ladder.view.OutputView; + +public class Application { + public static void main(String[] args) { + Names names = new Names(InputView.readNames()); + Height height = new Height(InputView.readHeight()); + + Lines lines = new Lines(height, names.size(), new RandomLineGenerator()); + Ladder ladder = new Ladder(names, lines); + + OutputView.printResult(ladder); + } +} diff --git a/src/main/java/nextstep/ladder/domain/BooleanGenerator.java b/src/main/java/nextstep/ladder/domain/BooleanGenerator.java new file mode 100644 index 0000000000..5ee9898072 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/BooleanGenerator.java @@ -0,0 +1,6 @@ +package nextstep.ladder.domain; + +@FunctionalInterface +public interface BooleanGenerator { + boolean generate(); +} diff --git a/src/main/java/nextstep/ladder/domain/Height.java b/src/main/java/nextstep/ladder/domain/Height.java new file mode 100644 index 0000000000..9bec07de16 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Height.java @@ -0,0 +1,11 @@ +package nextstep.ladder.domain; + +public record Height(int value) { + private static final int MIN_HEIGHT = 1; + + public Height { + if (value < MIN_HEIGHT) { + throw new IllegalArgumentException("높이는 %d이상이어야 합니다. 입력값: %d".formatted(MIN_HEIGHT, value)); + } + } +} diff --git a/src/main/java/nextstep/ladder/domain/Ladder.java b/src/main/java/nextstep/ladder/domain/Ladder.java new file mode 100644 index 0000000000..0c6f1515bc --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Ladder.java @@ -0,0 +1,8 @@ +package nextstep.ladder.domain; + +public record Ladder(Names names, Lines lines) { + + public String toDisplay() { + return names.toDisplay() + "\n" + lines.toDisplay(); + } +} diff --git a/src/main/java/nextstep/ladder/domain/Line.java b/src/main/java/nextstep/ladder/domain/Line.java new file mode 100644 index 0000000000..22486906e8 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Line.java @@ -0,0 +1,41 @@ +package nextstep.ladder.domain; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public record Line(List points) { + private static final String CONNECTED = "-----|"; + private static final String EMPTY = " |"; + + public Line(Boolean... inputs) { + this(List.of(inputs)); + } + + public Line { + validate(points); + points = List.copyOf(points); + } + + private void validate(List inputs) { + if (inputs == null || inputs.isEmpty()) { + throw new IllegalArgumentException("가로선 정보는 필수입니다."); + } + + boolean hasConsecutive = + IntStream.range(0, inputs.size() - 1).anyMatch(i -> inputs.get(i) && inputs.get(i + 1)); + + if (hasConsecutive) { + throw new IllegalArgumentException("가로선은 연속될 수 없습니다."); + } + } + + public String toDisplay() { + String body = points.stream().map(this::toSegment).collect(Collectors.joining()); + return " |" + body; + } + + private String toSegment(boolean connected) { + return connected ? CONNECTED : EMPTY; + } +} diff --git a/src/main/java/nextstep/ladder/domain/LineGenerator.java b/src/main/java/nextstep/ladder/domain/LineGenerator.java new file mode 100644 index 0000000000..a27f350010 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/LineGenerator.java @@ -0,0 +1,6 @@ +package nextstep.ladder.domain; + +@FunctionalInterface +public interface LineGenerator { + Line generate(int count); +} diff --git a/src/main/java/nextstep/ladder/domain/Lines.java b/src/main/java/nextstep/ladder/domain/Lines.java new file mode 100644 index 0000000000..8f713bada6 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Lines.java @@ -0,0 +1,30 @@ +package nextstep.ladder.domain; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public record Lines(List values) { + + public Lines(int height, int personCount, LineGenerator generator) { + this(new Height(height), personCount, generator); + } + + public Lines(Height height, int personCount, LineGenerator generator) { + this(generate(height, personCount, generator)); + } + + private static List generate(Height height, int personCount, LineGenerator generator) { + return IntStream.range(0, height.value()) + .mapToObj(i -> generator.generate(personCount - 1)) + .toList(); + } + + public Lines(List values) { + this.values = List.copyOf(values); + } + + public String toDisplay() { + return values.stream().map(Line::toDisplay).collect(Collectors.joining("\n")); + } +} diff --git a/src/main/java/nextstep/ladder/domain/Name.java b/src/main/java/nextstep/ladder/domain/Name.java new file mode 100644 index 0000000000..e823628572 --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Name.java @@ -0,0 +1,19 @@ +package nextstep.ladder.domain; + +public record Name(String value) { + private static final int MAX_LENGTH = 5; + + public Name { + if (value == null || value.isBlank()) { + throw new IllegalArgumentException("이름은 필수입니다. 입력값: " + value); + } + + if (value.length() > MAX_LENGTH) { + throw new IllegalArgumentException("이름은 %d자 이하여야 합니다. 입력값: %s".formatted(MAX_LENGTH, value)); + } + } + + public String toDisplay() { + return String.format("%-6s", value); + } +} diff --git a/src/main/java/nextstep/ladder/domain/Names.java b/src/main/java/nextstep/ladder/domain/Names.java new file mode 100644 index 0000000000..9293621ddf --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/Names.java @@ -0,0 +1,37 @@ +package nextstep.ladder.domain; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public record Names(List values) { + private static final int MIN_SIZE = 2; + + public Names(String input) { + this(Arrays.stream(input.split(",")).map(Name::new).toList()); + } + + public Names(List values) { + validate(values); + + this.values = List.copyOf(values); + } + + private void validate(List values) { + if (values == null) { + throw new IllegalArgumentException("참가자는 필수입니다."); + } + + if (values.size() < MIN_SIZE) { + throw new IllegalArgumentException("참가자는 %d명 이상이어야 합니다. 입력 인원: %d".formatted(MIN_SIZE, values.size())); + } + } + + public int size() { + return values.size(); + } + + public String toDisplay() { + return values.stream().map(Name::toDisplay).collect(Collectors.joining()); + } +} diff --git a/src/main/java/nextstep/ladder/domain/RandomLineGenerator.java b/src/main/java/nextstep/ladder/domain/RandomLineGenerator.java new file mode 100644 index 0000000000..9ccbe1ab9e --- /dev/null +++ b/src/main/java/nextstep/ladder/domain/RandomLineGenerator.java @@ -0,0 +1,30 @@ +package nextstep.ladder.domain; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class RandomLineGenerator implements LineGenerator { + private final BooleanGenerator booleanGenerator; + + public RandomLineGenerator() { + this(new Random()::nextBoolean); + } + + public RandomLineGenerator(BooleanGenerator booleanGenerator) { + this.booleanGenerator = booleanGenerator; + } + + @Override + public Line generate(int count) { + List points = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + boolean prev = i > 0 && points.get(i - 1); + boolean current = !prev && booleanGenerator.generate(); + points.add(current); + } + + return new Line(points); + } +} diff --git a/src/main/java/nextstep/ladder/view/InputView.java b/src/main/java/nextstep/ladder/view/InputView.java new file mode 100644 index 0000000000..f540beeabe --- /dev/null +++ b/src/main/java/nextstep/ladder/view/InputView.java @@ -0,0 +1,17 @@ +package nextstep.ladder.view; + +import java.util.Scanner; + +public class InputView { + private static final Scanner SCANNER = new Scanner(System.in); + + public static String readNames() { + System.out.println("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"); + return SCANNER.nextLine(); + } + + public static int readHeight() { + System.out.println("최대 사다리 높이는 몇 개인가요?"); + return Integer.parseInt(SCANNER.nextLine()); + } +} diff --git a/src/main/java/nextstep/ladder/view/OutputView.java b/src/main/java/nextstep/ladder/view/OutputView.java new file mode 100644 index 0000000000..3062629f26 --- /dev/null +++ b/src/main/java/nextstep/ladder/view/OutputView.java @@ -0,0 +1,11 @@ +package nextstep.ladder.view; + +import nextstep.ladder.domain.Ladder; + +public class OutputView { + + public static void printResult(Ladder ladder) { + System.out.println("\n실행결과\n"); + System.out.println(ladder.toDisplay()); + } +} diff --git a/src/test/java/nextstep/ladder/domain/HeightTest.java b/src/test/java/nextstep/ladder/domain/HeightTest.java new file mode 100644 index 0000000000..b3bb18bc34 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/HeightTest.java @@ -0,0 +1,23 @@ +package nextstep.ladder.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class HeightTest { + + @Test + void 생성자_정상입력_생성성공() { + assertThatCode(() -> new Height(1)).doesNotThrowAnyException(); + } + + @Test + void 생성자_1미만_예외발생() { + assertThatThrownBy(() -> new Height(0)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("높이는 1이상이어야 합니다."); + } +} diff --git a/src/test/java/nextstep/ladder/domain/LadderTest.java b/src/test/java/nextstep/ladder/domain/LadderTest.java new file mode 100644 index 0000000000..12838b38ef --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/LadderTest.java @@ -0,0 +1,20 @@ +package nextstep.ladder.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class LadderTest { + @Test + void toDisplay() { + Names names = new Names("pobi,honux,jk"); + Lines lines = new Lines(2, 3, count -> new Line(true, false)); + Ladder ladder = new Ladder(names, lines); + + assertThat(ladder.toDisplay()) + .isEqualTo("pobi honux jk \n" + " |-----| |\n" + " |-----| |"); + } +} diff --git a/src/test/java/nextstep/ladder/domain/LineTest.java b/src/test/java/nextstep/ladder/domain/LineTest.java new file mode 100644 index 0000000000..27934e9463 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/LineTest.java @@ -0,0 +1,39 @@ +package nextstep.ladder.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class LineTest { + + @Test + void 생성자_정상입력_생성성공() { + assertThat(new Line(true, false, true).points()).containsExactly(true, false, true); + } + + @ParameterizedTest(name = "빈값:{0}") + @NullAndEmptySource + void 생성자_빈값_예외발생(List inputs) { + assertThatThrownBy(() -> new Line(inputs)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("가로선 정보는 필수입니다."); + } + + @Test + void 생성자_연속된가로선_예외발생() { + assertThatThrownBy(() -> new Line(List.of(true, true))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("가로선은 연속될 수 없습니다."); + } + + @Test + void toDisplay() { + assertThat(new Line(true, false, true).toDisplay()).isEqualTo(" |-----| |-----|"); + } +} diff --git a/src/test/java/nextstep/ladder/domain/LinesTest.java b/src/test/java/nextstep/ladder/domain/LinesTest.java new file mode 100644 index 0000000000..189d8c3380 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/LinesTest.java @@ -0,0 +1,22 @@ +package nextstep.ladder.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class LinesTest { + + @Test + void 생성자_Height만큼_Line생성() { + assertThat(new Lines(3, 3, count -> new Line(true, false)).values()).hasSize(3); + } + + @Test + void toDisplay() { + assertThat(new Lines(2, 3, count -> new Line(true, false)).toDisplay()) + .isEqualTo(" |-----| |\n |-----| |"); + } +} diff --git a/src/test/java/nextstep/ladder/domain/NameTest.java b/src/test/java/nextstep/ladder/domain/NameTest.java new file mode 100644 index 0000000000..297c8eed07 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/NameTest.java @@ -0,0 +1,41 @@ +package nextstep.ladder.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class NameTest { + + @ParameterizedTest(name = "올바른 값:{0}") + @ValueSource(strings = {"일", "일이삼사오"}) + void 생성자_정상입력_생성성공(String input) { + assertThatCode(() -> new Name(input)).doesNotThrowAnyException(); + } + + @ParameterizedTest(name = "빈값:{0}") + @NullAndEmptySource + @ValueSource(strings = " ") + void 생성자_빈값_예외발생(String input) { + assertThatThrownBy(() -> new Name(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("이름은 필수입니다."); + } + + @Test + void 생성자_5글자초과_예외발생() { + assertThatThrownBy(() -> new Name("123456")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("이름은 5자 이하여야 합니다."); + } + + @Test + void toDisplay_6자고정_오른쪽공백채움() { + assertThat(new Name("사과").toDisplay()).isEqualTo("사과 "); + } +} diff --git a/src/test/java/nextstep/ladder/domain/NamesTest.java b/src/test/java/nextstep/ladder/domain/NamesTest.java new file mode 100644 index 0000000000..2f0310a1c4 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/NamesTest.java @@ -0,0 +1,36 @@ +package nextstep.ladder.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class NamesTest { + + @Test + void 생성자_쉼표구분파싱() { + assertThat(new Names("사과,바나나")).isEqualTo(new Names(List.of(new Name("사과"), new Name("바나나")))); + } + + @Test + void 생성자_null_예외발생() { + assertThatThrownBy(() -> new Names((List) null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("참가자는 필수"); + } + + @Test + void 생성자_2명미만_예외발생() { + assertThatThrownBy(() -> new Names(List.of(new Name("사과")))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("참가자는 2명 이상"); + } + + @Test + void toDisplay() { + assertThat(new Names("맛있는사과,바나나,키위").toDisplay()).isEqualTo("맛있는사과 바나나 키위 "); + } +} diff --git a/src/test/java/nextstep/ladder/domain/RandomLineGeneratorTest.java b/src/test/java/nextstep/ladder/domain/RandomLineGeneratorTest.java new file mode 100644 index 0000000000..81f9e4f132 --- /dev/null +++ b/src/test/java/nextstep/ladder/domain/RandomLineGeneratorTest.java @@ -0,0 +1,23 @@ +package nextstep.ladder.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Iterator; +import java.util.List; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class RandomLineGeneratorTest { + + @Test + void generate_연속true_방지() { + Iterator values = List.of(true, true, true).iterator(); + BooleanGenerator generator = values::next; + + Line line = new RandomLineGenerator(generator).generate(3); + + assertThat(line.points()).containsExactly(true, false, true); + } +}