Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b374be4
docs(README.md): 구현기능 목록 작성
IAGREEBUT Nov 2, 2024
114bf5f
feat : 로또 구입금액 입력
IAGREEBUT Nov 2, 2024
ef6e551
test : 로또 구입금액 단위 테스트
IAGREEBUT Nov 2, 2024
a3f96a4
refactor(purchasePrice) : 로또 구입 금액 유효체크 utils로 변경
IAGREEBUT Nov 2, 2024
04675d3
feat : 구매한 로또의 총 갯수를 구하고, 문구를 출력
IAGREEBUT Nov 2, 2024
4dbf7ec
docs(README.md): 구현기능 에러 케이스 추가 (로또 번호 생성)
IAGREEBUT Nov 3, 2024
025923a
feat : 구매한 로또들의 랜덤 번호 생성
IAGREEBUT Nov 3, 2024
ddee4b8
test(Lotto) : 로또 클래스 테스트 추가
IAGREEBUT Nov 3, 2024
9733435
refactor : 로또 유효성 검사 validationCheck 객체 사용
IAGREEBUT Nov 3, 2024
05eeb53
refactor : purchasePrice 유효성검사 수정
IAGREEBUT Nov 3, 2024
909879d
feat : 로또 당첨 번호를 입력 및 유효성 검증
IAGREEBUT Nov 3, 2024
438b1a3
test(winningNumber) : 당첨 번호 유효성 단위 테스트
IAGREEBUT Nov 3, 2024
04792a8
feat : 보너스 번호를 입력
IAGREEBUT Nov 3, 2024
c9fbb8b
test(bonusNumber) : 보너스번호 유효성 검사
IAGREEBUT Nov 3, 2024
f00ee19
fixed : 당첨번호 number array로 변경
IAGREEBUT Nov 3, 2024
6c60e77
feat : 당첨 통계구하기
IAGREEBUT Nov 3, 2024
3154fa3
test : 당첨 통계 테스트
IAGREEBUT Nov 3, 2024
e37654e
feat : 수익률 계산
IAGREEBUT Nov 3, 2024
ae65245
test : 수익률 계산 테스트
IAGREEBUT Nov 3, 2024
5911aba
fixed : 양의 정수 체크시 공백 오류 해결
IAGREEBUT Nov 3, 2024
c7186f7
fixed : 테스트 형식을 위해 array출력 형식 변경
IAGREEBUT Nov 3, 2024
1ac683d
chore : 공백, 줄 제거 등
IAGREEBUT Nov 3, 2024
5e38c73
refactor : validation 파일 통합
IAGREEBUT Nov 3, 2024
899601e
refactor : output(출력) 관련 분리
IAGREEBUT Nov 3, 2024
c4737a2
refactor : 로또 결과 계산 및 출력 부분 함수화
IAGREEBUT Nov 3, 2024
0699199
refactor : 출력은 IOHandler에서만 담당하도록 변경
IAGREEBUT Nov 4, 2024
1ea02c3
refactor : default 값 사용대신 값이 있는지 체크
IAGREEBUT Nov 4, 2024
92f6884
refactor : depth 2 제한 수정
IAGREEBUT Nov 4, 2024
f417c71
refactor(Lotto.js) : private method로 변경
IAGREEBUT Nov 4, 2024
b42fcf9
refactor : LottoGame class 추가
IAGREEBUT Nov 4, 2024
17ee04d
test : lottoGame class추가
IAGREEBUT Nov 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 62 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,62 @@
# javascript-lotto-precourse
# [3주차] 로또

----

## 1. 구현할 기능 목록

----

### 1. 입력값 받기 : 로또 구입 금액 입력받기
### 2. 구매한 로또의 총 갯수를 구하고, 문구를 출력한다.
### 3. 구매한 로또들의 랜덤 번호 생성
- 구매한 로또번호들의 6가지 랜덤 번호를 생성한다
- 오름차순으로 정렬하여 저장한다
- 구매한 로또 번호를 출력한다.
### 4. 당첨 번호를 입력받는다
### 5. 보너스 번호를 입력받는다.
### 6. 당첨 통계를 구한다
- 각각의 index가 일치한 숫자의 갯수를 의미하는 길이가 8인 배열을 만든다. (index = 6은 2등을, index = 7은 1등을 의미한다.)
- 구매한 로또들을 순회
- 하나의 로또에서 몇개의 번호가 맞았는지 구한다.
- 배열[일치한 갯수] 에 값을 증가시킨다.
- 만약 5개 맞았다면, 보너스 번호 일치 여부를 확인한다
- 만약 6개 맞았다면, index = 7에 저장한다.
### 7. 수익률을 계산한다

- 배열을 사용하여 (index = 7 은 1등 ... index = 3 은 5등) 당첨금의 합을 구한다
- 당첨금/지불한 값으로 수익률을 계산한다
- 소수점 둘째 자리에서 반올림한다

----

## 2. 예외 케이스
### 1. 로또 구입 금액 입력받기
- 1000원 단위 금액이 아닌 경우
- 양의 정수를 입력했는가(/^[1-9]\d*$/)
- 최대값 제한 (SAFE INTEGER 범위)

### 3. 구매한 로또들의 랜덤번호 생성
- 로또 번호의 수가 6개인지 확인
- 로또 번호에 중복이 없는지 확인

### 4. 로또 당첨 번호 입력
- 당첨번호가 6개가 아닌 경우
- 양의 정수를 입력했는가 (/^[1-9]\d*$/)
- 당첨번호가 유효범위 내에 있지 않은 경우 (1~45의 정수가 아님)
- 중복된 수를 입력한 경우

### 5. 보너스 번호 입력
- 양의 정수를 입력했는가
- 당첨번호가 유효범위 내에 있지 않은 경우
- 당첨 번호와 중복되는지 여부

---

## 3. 고민한 부분
- 지난 회차에서 미흡하다고 생각했던 Unit Test를 상세히 추가했습니다.
특히, validation Check를 꼼꼼하게 하지 못했다고 생각해서, 검사하는 부분 및 순서에 시간을 많이 사용하였습니다. 또한, 해당 부분을 중점으로 unit test를 좀 더 꼼꼼하게 작성하였습니다.
- 상수화에 대한 고민. 상수화를 최대한 하되, 구분이 되도록 constant.js 파일 내에서도 관련 그룹으로 묶어 용도를 명확히 하고자 노력했습니다.
- 책임의 분리. 어떤 객체, 클래스에 책임을 두어야 할지 고민하면서, 객체가 너무 많아지지 않도록 여러번의 refactoring을 거쳤습니다.

이번 과제는 구현하면서 세세한 부분이 생각보다 많아, 제가 생각한 것보다 코드를 깔끔하게 작성하기 어려웠습니다. <br/>
또한, 코드와 함수가 많아지니 책임의 분리가 어려웠습니다. 부족한 코드지만 많은 피드백 부탁드립니다.
146 changes: 74 additions & 72 deletions __tests__/ApplicationTest.js
Original file line number Diff line number Diff line change
@@ -1,97 +1,99 @@
import App from "../src/App.js";
import { MissionUtils } from "@woowacourse/mission-utils";
import {MissionUtils} from "@woowacourse/mission-utils";
import {ERROR_CODE} from "../src/constants/constants.js";

const mockQuestions = (inputs) => {
MissionUtils.Console.readLineAsync = jest.fn();
export const mockQuestions = (inputs) => {
MissionUtils.Console.readLineAsync = jest.fn();

MissionUtils.Console.readLineAsync.mockImplementation(() => {
const input = inputs.shift();
MissionUtils.Console.readLineAsync.mockImplementation(() => {
const input = inputs.shift();

return Promise.resolve(input);
});
return Promise.resolve(input);
});
};

const mockRandoms = (numbers) => {
MissionUtils.Random.pickUniqueNumbersInRange = jest.fn();
numbers.reduce((acc, number) => {
return acc.mockReturnValueOnce(number);
}, MissionUtils.Random.pickUniqueNumbersInRange);
MissionUtils.Random.pickUniqueNumbersInRange = jest.fn();
numbers.reduce((acc, number) => {
return acc.mockReturnValueOnce(number);
}, MissionUtils.Random.pickUniqueNumbersInRange);
};

const getLogSpy = () => {
const logSpy = jest.spyOn(MissionUtils.Console, "print");
logSpy.mockClear();
return logSpy;
const logSpy = jest.spyOn(MissionUtils.Console, "print");
logSpy.mockClear();
return logSpy;
};

const runException = async (input) => {
// given
const logSpy = getLogSpy();

const RANDOM_NUMBERS_TO_END = [1, 2, 3, 4, 5, 6];
const INPUT_NUMBERS_TO_END = ["1000", "1,2,3,4,5,6", "7"];

mockRandoms([RANDOM_NUMBERS_TO_END]);
mockQuestions([input, ...INPUT_NUMBERS_TO_END]);

// when
const app = new App();
await app.run();

// then
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("[ERROR]"));
};

describe("로또 테스트", () => {
beforeEach(() => {
jest.restoreAllMocks();
});

test("기능 테스트", async () => {
// given
const logSpy = getLogSpy();

mockRandoms([
[8, 21, 23, 41, 42, 43],
[3, 5, 11, 16, 32, 38],
[7, 11, 16, 35, 36, 44],
[1, 8, 11, 31, 41, 42],
[13, 14, 16, 38, 42, 45],
[7, 11, 30, 40, 42, 43],
[2, 13, 22, 32, 38, 45],
[1, 3, 5, 14, 22, 45],
]);
mockQuestions(["8000", "1,2,3,4,5,6", "7"]);
const RANDOM_NUMBERS_TO_END = [1, 2, 3, 4, 5, 6];
const INPUT_NUMBERS_TO_END = ["1000", "1,2,3,4,5,6", "7"];

mockRandoms([RANDOM_NUMBERS_TO_END]);
mockQuestions([input, ...INPUT_NUMBERS_TO_END]);

// when
const app = new App();
await app.run();

// then
const logs = [
"8개를 구매했습니다.",
"[8, 21, 23, 41, 42, 43]",
"[3, 5, 11, 16, 32, 38]",
"[7, 11, 16, 35, 36, 44]",
"[1, 8, 11, 31, 41, 42]",
"[13, 14, 16, 38, 42, 45]",
"[7, 11, 30, 40, 42, 43]",
"[2, 13, 22, 32, 38, 45]",
"[1, 3, 5, 14, 22, 45]",
"3개 일치 (5,000원) - 1개",
"4개 일치 (50,000원) - 0개",
"5개 일치 (1,500,000원) - 0개",
"5개 일치, 보너스 볼 일치 (30,000,000원) - 0개",
"6개 일치 (2,000,000,000원) - 0개",
"총 수익률은 62.5%입니다.",
];

logs.forEach((log) => {
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(log));
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("[ERROR]"));
};

describe("로또 테스트", () => {
beforeEach(() => {
jest.restoreAllMocks();
});
});

test("예외 테스트", async () => {
await runException("1000j");
});
test("기능 테스트", async () => {
// given
const logSpy = getLogSpy();

mockRandoms([
[8, 21, 23, 41, 42, 43],
[3, 5, 11, 16, 32, 38],
[7, 11, 16, 35, 36, 44],
[1, 8, 11, 31, 41, 42],
[13, 14, 16, 38, 42, 45],
[7, 11, 30, 40, 42, 43],
[2, 13, 22, 32, 38, 45],
[1, 3, 5, 14, 22, 45],
]);
mockQuestions(["8000", "1,2,3,4,5,6", "7"]);

// when
const app = new App();
await app.run();

// then
const logs = [
"8개를 구매했습니다.",
"[8, 21, 23, 41, 42, 43]",
"[3, 5, 11, 16, 32, 38]",
"[7, 11, 16, 35, 36, 44]",
"[1, 8, 11, 31, 41, 42]",
"[13, 14, 16, 38, 42, 45]",
"[7, 11, 30, 40, 42, 43]",
"[2, 13, 22, 32, 38, 45]",
"[1, 3, 5, 14, 22, 45]",
"3개 일치 (5,000원) - 1개",
"4개 일치 (50,000원) - 0개",
"5개 일치 (1,500,000원) - 0개",
"5개 일치, 보너스 볼 일치 (30,000,000원) - 0개",
"6개 일치 (2,000,000,000원) - 0개",
"총 수익률은 62.5%입니다.",
];

logs.forEach((log) => {
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining(log));
});
});


test("예외 테스트", async () => {
await runException("1000j");
});
});
Loading