-
Notifications
You must be signed in to change notification settings - Fork 197
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
[클린코드 7기 김동진] 자동차 경주 미션 STEP 4 #318
base: terrydkim
Are you sure you want to change the base?
Changes from all commits
ae072b3
5711c6a
fbfce55
7306715
a3f90fa
5586943
4e199a9
7020410
e86c2de
39687df
30e9716
ecab91f
b62b20b
54cc93a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import Input from "../src/view/Input"; | ||
import { CAR_NAMES } from "../src/utils/constants"; | ||
|
||
describe("Input 유효성 메서드 테스트", () => { | ||
let input; | ||
beforeEach(() => { | ||
input = new Input(); | ||
}); | ||
|
||
describe("이름 안에 공백이 포함된 경우", () => { | ||
it("이름에 공백이 있으면 false를 반환한다.", () => { | ||
const validate = input.isValidNotSpace([CAR_NAMES.NAME_SPACE]); | ||
expect(validate).toBeFalsy(); | ||
}); | ||
|
||
it("이름에 공백이 없으면 true를 반환한다.", () => { | ||
const validate = input.isValidNotSpace([CAR_NAMES.NAME]); | ||
expect(validate).toBeTruthy(); | ||
}); | ||
}); | ||
|
||
describe("SplitBy 메서드", () => { | ||
it("쉼표로 구분된 문자열을 배열로 변환한다.", () => { | ||
const result = input.splitBy("G70,GV80,NEXT_STEP", ","); | ||
expect(result).toEqual([CAR_NAMES.G70, CAR_NAMES.GV80, CAR_NAMES.NEXT_STEP]); | ||
}); | ||
|
||
it("-로 구분된 문자열을 배열로 변환한다.", () => { | ||
const result = input.splitBy("G70-GV80-NEXT_STEP", "-"); | ||
expect(result).toEqual([CAR_NAMES.G70, CAR_NAMES.GV80, CAR_NAMES.NEXT_STEP]); | ||
}); | ||
|
||
it("이름의 앞 뒤 공백을 제거하고 배열로 변환한다.", () => { | ||
const result = input.splitBy(" G70,GV80 , NEXT_STEP ", ","); | ||
expect(result).toEqual([CAR_NAMES.G70, CAR_NAMES.GV80, CAR_NAMES.NEXT_STEP]); | ||
}); | ||
}); | ||
|
||
describe("경주 횟수 입력 유효성 검사", () => { | ||
it("사용자가 양수를 입력하면 true를 반환한다.", () => { | ||
const result = input.isValidInteger(1); | ||
expect(result).toBeTruthy(); | ||
}); | ||
|
||
describe("사용자가 양수가 아닌", () => { | ||
it("0 입력 시 false를 반환한다.", () => { | ||
const result = input.isValidInteger(0); | ||
expect(result).toBeFalsy(); | ||
}); | ||
|
||
it("음수 입력 시 false를 반환한다.", () => { | ||
const result = input.isValidInteger(-1); | ||
expect(result).toBeFalsy(); | ||
}); | ||
|
||
it("유리수 입력 시 false를 반환한다.", () => { | ||
const result = input.isValidInteger(1.11); | ||
expect(result).toBeFalsy(); | ||
}); | ||
|
||
it("String 입력 시 false를 반환한다.", () => { | ||
const result = input.isValidInteger("일"); | ||
expect(result).toBeFalsy(); | ||
}); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
## 요구사항 | ||
|
||
- [ ] 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다. | ||
- [ ] 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다. | ||
- [ ] 자동차 경주는 5회로 고정하여 진행한다. | ||
- [ ] 자동차는 1회당 1칸씩 전진한다 | ||
- [ ] 회차를 거듭할 때마다 자동차가 지나간 궤적을 출력한다(실행 예시 참고). | ||
- [ ] 사용자가 잘못된 입력 값을 작성한 경우 프로그램을 종료한다. | ||
- [X] 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다. | ||
- [X] 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다. | ||
- [X] 자동차 경주 횟수는 사용자에게 입력 받는다. | ||
- [X] 자동차 경주는 사용자 입력이 없다면 5회로 고정하여 진행한다. | ||
- [X] 전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다. | ||
- [X] 자동차는 1회당 1칸씩 전진한다 | ||
- [X] 회차를 거듭할 때마다 자동차가 지나간 궤적을 출력한다(실행 예시 참고). | ||
- [X] 사용자가 잘못된 입력 값을 작성한 경우 프로그램을 종료한다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ | |
"vite": "^6.1.0" | ||
}, | ||
"jest": { | ||
"verbose": true, | ||
"transform": { | ||
"^.+\\.js$": "babel-jest" | ||
} | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,12 @@ | ||
import { ERROR_MESSAGES, NUMBERS } from "../utils/constants.js"; | ||
|
||
class Car { | ||
static INVALID_CAR_NAME = "잘못된 자동차 이름입니다."; | ||
#name; | ||
#location = 0; | ||
|
||
constructor(name) { | ||
if (!this.isValidName(name)) { | ||
throw new Error(Car.INVALID_CAR_NAME); | ||
throw new Error(ERROR_MESSAGES.INVALID_CAR_NAME); | ||
} | ||
this.#name = name; | ||
} | ||
|
@@ -22,9 +23,17 @@ class Car { | |
this.#location += 1; | ||
} | ||
|
||
movingCondition() { | ||
return this.getRandomNumber() >= NUMBERS.THRESHOLD; | ||
} | ||
|
||
isValidName(name) { | ||
return name.length >= 1 && name.length <= 5; | ||
} | ||
|
||
getRandomNumber() { | ||
return Math.floor(Math.random() * NUMBERS.MAX_RANGE); | ||
} | ||
Comment on lines
+34
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 랜덤한 숫자를 얻는 동작은 특정 도메인과 연관되기보다 유틸의 성격이 더 강하지 않을까요? 따로 분리하는 게 어떨까요? |
||
} | ||
|
||
export default Car; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,28 +4,47 @@ class Race { | |
rounds; | ||
result = []; | ||
|
||
constructor(cars) { | ||
constructor(cars, rounds = Race.DEFAULT_ROUNDS) { | ||
this.cars = cars; | ||
this.rounds = Race.DEFAULT_ROUNDS; | ||
this.rounds = rounds; | ||
} | ||
|
||
moveCars(cars) { | ||
cars.map((car) => car.moveForward()); | ||
cars.map((car) => { | ||
if (car.movingCondition()) { | ||
car.moveForward(); | ||
} | ||
}); | ||
} | ||
|
||
// 자동차는 1회에 1칸씩 이동 | ||
start() { | ||
for (let round = 1; round <= this.rounds; round++) { | ||
this.moveCars(this.cars); | ||
|
||
this.result.push({ | ||
round: round, | ||
cars: this.cars.map((car) => ({ | ||
name: car.getName(), | ||
location: car.getLocation(), | ||
})), | ||
}); | ||
this.recordRoundResult(round, this.cars); | ||
Comment on lines
22
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. moveCars, recordRoundResult에서도 |
||
} | ||
return this.result; | ||
} | ||
|
||
recordRoundResult(round, cars) { | ||
this.result.push({ | ||
round: round, | ||
|
||
cars: cars.map((car) => ({ | ||
name: car.getName(), | ||
location: car.getLocation(), | ||
})), | ||
}); | ||
} | ||
|
||
getWinners() { | ||
const locations = this.cars.map((car) => car.getLocation()); | ||
const maxLocation = Math.max(...locations); | ||
|
||
const winnersCar = this.cars.filter( | ||
(car) => car.getLocation() === maxLocation | ||
); | ||
|
||
return winnersCar.map((car) => car.getName()); | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NUMBERS
라는 이름과 접근자명은 다소 포괄적으로 보이는 데요,NUMBERS.THRESHOLD
- '차가 움직일 수 있는 조건'의 역할을 가지고 있는데, 이것이 이름에 내포되어 있지 않아 사용처에서 어떻게 사용되는지로만 유추할 수 있어 보여요.NUMBERS.MAX_RANGE
- 마찬가지로 어떠한 부분에 대한 max range인지 선언부만 봐서는 알기 힘들지 않을까요?