Skip to content
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

[로또] 홍서진 미션 제출합니다 #644

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
625e1d6
chore : eslint, prettier 설정
Nov 7, 2023
e475c69
docs : 기능 구현 목록 작성
Nov 7, 2023
1121ac7
docs : (S-0-1) 클래스와 객체의 개념 사전 학습
Nov 7, 2023
a78754e
docs : (S-0-2) 클래스와 객체를 분리하는 방법 사전 학습
Nov 7, 2023
f587cad
docs : (S-0-3) 도메인 로직의 개념 사전 학습
Nov 7, 2023
d29141b
docs : (S-0-4) 도메인 로직의 단위 테스트 사전 학습
Nov 8, 2023
807256a
feat : (T-1-1) 금액 입력 메세지 출력 테스트 구현
Nov 8, 2023
9f86b8f
feat : (C-1-1) 금액 입력 메세지 출력 기능 구현
Nov 8, 2023
e92f302
feat : (T-1-2) 금액 입력 처리 테스트 구현
Nov 8, 2023
83b0e2b
feat : (C-1-2) 금액 입력 처리 기능 구현
Nov 8, 2023
fa5f3ee
docs : (C-1-2) 금액 입력 처리 기능 목록 추가
Nov 8, 2023
beee629
feat : (C-1-2) 금액 입력 공백 처리 기능 구현
Nov 8, 2023
2f442f8
feat : (T-1-3) 금액 입력 예외 처리 테스트 구현
Nov 8, 2023
c9d5c0d
feat : (C-1-3) 금액 입력 예외 처리 기능 구현
Nov 8, 2023
6b9e8cf
chore : 테스트 파일명 변경
Nov 8, 2023
e8a9cac
feat : (T-2-1) 구입 금액에 따른 로또 발행 테스트 구현
Nov 8, 2023
6c30847
feat : (C-2-1) 구입 금액에 따른 로또 발행 기능 구현
Nov 8, 2023
c12553b
feat : (T-2-2) 로또 번호 중복 없이 생성 테스트 구현
Nov 8, 2023
ca6a68b
feat : (C-2-2) 로또 번호 중복 없이 생성 기능 구현
Nov 8, 2023
4c012a6
feat : (T-2-3) 발행한 로또 수량 및 번호 출력 테스트 구현
Nov 8, 2023
67d9662
refactor : (T-2-3) 발행한 로또 수량 및 번호 출력 테스트 수정
Nov 8, 2023
ff660fb
feat : (C-2-3) 발행한 로또 수량 및 번호 출력 기능 구현
Nov 8, 2023
4fce34d
feat : 나머지 기능 구현
Nov 8, 2023
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
31 changes: 31 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export default {
env: {
browser: true,
es2021: true,
},
extends: ['airbnb-base', 'prettier'],
overrides: [
{
env: {
jest: true,
node: true,
},
files: [
'.eslintrc.{js,cjs}',
],
parserOptions: {
sourceType: 'script',
},
},
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
rules: {
'max-depth': ['error', 2],
'max-params': ['error', 3],
'max-lines-per-function': ['error', { max: 10 }],
'import/extensions': 'off',
},
};
11 changes: 11 additions & 0 deletions .prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default {
singleQuote: true,
semi: true,
useTabs: false,
tabWidth: 2,
trailingComma: "all",
printWidth: 80,
bracketSpacing: true,
arrowParens: "always",
endOfLine: "auto",
};
52 changes: 52 additions & 0 deletions __tests__/InputManagerTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import InputManager from "../src/InputManager.js";
import { Console } from "@woowacourse/mission-utils"

jest.mock("@woowacourse/mission-utils", () => ({
Console: {
readLineAsync: jest.fn()
}
}));

describe("InputManager class", () => {
let inputManager;

beforeEach(() => {
jest.clearAllMocks();
inputManager = new InputManager();
});

describe("T-1-1 금액 입력 메세지 출력", () => {
test("사용자에게 금액 입력 메세지를 출력해야 한다", async () => {
Console.readLineAsync.mockResolvedValue("1000");
await inputManager.enterAmount();
expect(Console.readLineAsync).toHaveBeenCalledWith("구입금액을 입력해 주세요.\n");
});
});

describe("T-1-2 금액 입력 처리", () => {
test("입력받은 금액이 1,000원 단위일 때 올바르게 처리해야 한다", async () => {
const validAmount = "8000";
Console.readLineAsync.mockResolvedValue(validAmount);

const amount = await inputManager.enterAmount();
expect(Console.readLineAsync).toHaveBeenCalled();
expect(amount).toBe(parseInt(validAmount, 10));
});
});

describe("T-1-3 금액 입력 예외 처리", () => {
test("금액이 숫자가 아닌 경우 예외를 던져야 한다", async () => {
const invalidAmount = "abc";
Console.readLineAsync.mockResolvedValue(invalidAmount);

await expect(inputManager.enterAmount()).rejects.toThrow("[ERROR] 구입 금액은 숫자여야 합니다.");
});

test("1,000원으로 나누어 떨어지지 않을 경우 예외를 던져야 한다", async () => {
const invalidAmount = "1500";
Console.readLineAsync.mockResolvedValue(invalidAmount);

await expect(inputManager.enterAmount()).rejects.toThrow("[ERROR] 구입 금액은 1,000원 단위로 입력해야 합니다.");
});
});
});
30 changes: 29 additions & 1 deletion __tests__/LottoTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,33 @@ describe("로또 클래스 테스트", () => {
}).toThrow("[ERROR]");
});

// 아래에 추가 테스트 작성 가능
describe("T-2-1 구입 금액에 따른 로또 발행 테스트", () => {
test("입력받은 금액에 따라 적절한 수의 로또가 발행되어야 한다", () => {
const amount = 5000;
const lottos = Lotto.generateMultipleLottos(amount);
expect(lottos).toHaveLength(amount / 1000);
});
});

describe("T-2-2 로또 번호 중복 없이 생성 테스트", () => {
test("각 로또 번호는 6개이며 서로 중복되지 않아야 한다", () => {
const numbers = Lotto.generateNumbers();
expect(numbers).toHaveLength(6);
expect(new Set(numbers).size).toBe(6);
});
});

describe("T-2-3 발행한 로또 수량 및 번호 출력 테스트", () => {
test("발행한 로또 수량과 번호가 콘솔에 출력되어야 한다", () => {
const consoleSpy = jest.spyOn(console, 'log');
const lottos = Lotto.generateMultipleLottos(3000);
Lotto.printLottos(lottos);
expect(consoleSpy).toHaveBeenCalledWith("3개를 구매했습니다.");
lottos.forEach(lotto => {
expect(consoleSpy).toHaveBeenCalledWith(`[${lotto.numbers.join(', ')}]`);
});
consoleSpy.mockRestore();
});
});
});

219 changes: 219 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
# 💲 로또 게임 💲

![image](https://github.com/woowacourse-precourse/javascript-lotto-6/assets/146679818/1a60f055-13bd-41b7-89ab-7fa061216f70)

<br>

---

<br>

## 📢 3주차 학습 목표 📢

1. [] `클래스 (객체)를 분리`하는 연습
2. [] 도메인 로직에 대한 `단위 테스트`를 작성하는 연습
3. [] `함수를 분리`하는 연습
4. [] `함수별로 테스트`를 작성하는 연습

---

<br>

## 📖 사전 학습 목록 📖

0. [☑️] `클래스와 객체`에 대한 학습
- S-0-1. [✅] 클래스와 객체의 개념
- S-0-2. [✅] 클래스와 객체를 분리하는 방법

0. [☑️] `도메인 로직과 단위 테스트`에 대한 학습
- S-0-3. [✅] 도메인 로직의 개념
- S-0-4. [✅] 도메인 로직에 대한 단위 테스트를 작성하는 방법

---

<br>

## ✅ 기능 구현 목록 ✅

1. [☑️] `로또 구입 금액 입력 받기`
- C-1-1. [✅] 금액 입력 메세지 출력
- ex) "구입금액을 입력해 주세요."
- C-1-2. [✅] 금액 입력 처리
- 금액은 1,000원 단위로 입력
- 공백이 들어갈 경우 제거
- ex) 8000
- C-1-3. [✅] 금액 입력 예외 처리
- 금액이 숫자가 아닌 경우
- 1,000원으로 나누어 떨어지지 않을 경우
- 금액이 1,000원 미만인 경우
- throw문을 사용해 "[ERROR]" 로 시작하는 메세지를 가지는 예외 발생
- ex) [ERROR] 구입 금액은 1,000원 단위로 입력해야 합니다.

2. [☑️] `로또 번호 발행`
- C-2-1. [✅] 구입 금액에 따른 로또 발행
- 1,000원 단위로 로또 번호 자동 생성
- C-2-2. [✅] 로또 번호 중복 없이 생성
- 숫자 범위 1~45
- C-2-3. [✅] 발행한 로또 수량 및 번호 출력
- 번호는 오름차순으로 정렬하여 출력
- ex) "8개를 구매했습니다."

3. [] `당첨 번호 입력 받기`
- C-3-1. [] 당첨 번호 입력 메세지 출력
- ex) "당첨 번호를 입력해 주세요."
- C-3-2. [] 당첨 번호 입력 처리
- 번호는 쉼표로 구분하여 입력
- ex) 1,2,3,4,5,6
- C-3-3. [] 당첨 번호 입력 예외 처리
- 번호가 숫자가 아닌 경우
- 번호 범위가 1~45가 아닌 경우
- 번호가 중복된 경우
- 번호가 6개가 아닌 경우
- throw문을 사용해 "[ERROR]" 로 시작하는 메세지를 가지는 예외 발생
- ex) [ERROR] 로또 번호는 1부터 45 사이의 숫자여야 합니다.

4. [] `보너스 번호 입력 받기`
- C-4-1. [] 보너스 번호 입력 메세지 출력
- ex) "보너스 번호를 입력해 주세요."
- C-4-2. [] 보너스 번호 입력 처리
- 당첨 번호와 중복되지 않게 입력
- C-4-3. [] 보너스 번호 입력 예외 처리
- 번호가 숫자가 아닌 경우
- 번호 범위가 1~45가 아닌 경우
- 당첨 번호와 중복되는 경우
- throw문을 사용해 "[ERROR]" 로 시작하는 메세지를 가지는 예외 발생
- ex) [ERROR] 보너스 번호는 당첨 번호와 중복될 수 없습니다.

5. [] `당첨 결과 계산`
- C-5-1. [] 사용자의 로또 번호와 당첨 번호 비교
- C-5-2. [] 당첨 내역 계산
- 등수별 당첨 기준에 따른 당첨 내역 확인
- C-5-3. [] 수익률 계산
- 수익률은 구입 금액 대비 당첨 금액으로 계산

6. [] `당첨 결과 출력`
- C-6-1. [] 당첨 통계 메세지 출력
- ex) "당첨 통계"
- C-6-2. [] 당첨 내역 출력
- ex) "3개 일치 (5,000원) - 1개
- C-6-3. [] 수익 내역 출력
- 소수점 둘째 자리에서 반올림
- ex) "총 수익률은 62.5%입니다"

---

<br>

## 🔍 테스트 구현 목록 🔍

T-1. [☑️] `로또 구입 금액 입력 받기 테스트`
- T-1-1. [✅] 금액 입력 메세지 출력 테스트
- T-1-2. [✅] 금액 입력 처리 테스트
- T-1-3. [✅] 금액 입력 예외 처리 테스트

T-2. [☑️] `로또 번호 발행 테스트`
- T-2-1. [✅] 구입 금액에 따른 로또 발행 테스트
- T-2-2. [✅] 로또 번호 중복 없이 생성 테스트
- T-2-3. [✅] 발행한 로또 수량 및 번호 출력 테스트

T-3. [] `당첨 번호 입력 받기 테스트`
- T-3-1. [✅] 당첨 번호 입력 메세지 출력 테스트
- T-3-2. [] 당첨 번호 입력 처리 테스트
- T-3-3. [] 당첨 번호 입력 예외 처리 테스트

T-4. [] `보너스 번호 입력 받기 테스트`
- T-4-1. [] 보너스 번호 입력 메세지 출력 테스트
- T-4-2. [] 보너스 번호 입력 처리 테스트
- T-4-3. [] 보너스 번호 입력 예외 처리 테스트

T-5. [] `당첨 결과 계산 테스트`
- T-5-1. [] 사용자의 로또 번호와 당첨 번호 비교 테스트
- T-5-2. [] 당첨 내역 계산 테스트
- T-5-3. [] 수익률 계산 테스트

T-6. [] `당첨 결과 출력 테스트`
- T-6-1. [] 당첨 통계 메세지 출력 테스트
- T-6-2. [] 당첨 내역 출력 테스트
- T-6-3. [] 수익 내역 출력 테스트

---

<br>

## 🖋️ 요구사항 🖋️

- `Node.js 18.17.1 버전`에서 실행 가능해야 한다.
- 프로그램 실행의 `시작점은 App.js의 play 메서드`이다.
- `package.json`을 변경할 수 없다.
- `순수 Vanila JS`로만 구현한다.
- `외부 라이브러리`(jQuery, Lodash 등)를 사용하지 않는다.
- JavaScript `코드 컨벤션`을 지키면서 프로그래밍 한다
- 프로그램 종료 시 `process.exit()`를 호출하지 않는다
- 프로그램 구현이 완료되면 `ApplicationTest의 모든 테스트가 성공`해야 한다
- 요구 사항에서 달리 명시하지 않는 한 파일, 패키지 이름을 `수정하거나 이동하지 않는다`.
- `indent(인덴트, 들여쓰기) 2`까지만 허용한다.
- 예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
- `함수 (또는 메서드)가 한가지 일만` 하도록 최대한 작게 만든다.
- jest를 이용하여 본인이 정리한 기능 목록이 정상 동작함을 `테스트 코드로 확인`한다.
- `@woowacourse/mission-utils`에서 제공하는 Random 및 Console API를 사용하여 구현한다
- Random 값 추출은 `Random.pickNumberInRange()`를 활용한다.
- 사용자의 값을 입력 받고 출력하기 위해서는 `Console.readLineAsync, Console.print`를 활용한다

---

<br>

## 🖋️ 추가된 요구사항 🖋️

- `함수(또는 메서드)의 길이가 15라인`을 넘어가지 않도록 구현한다.
- `함수(또는 메서드)가 한 가지 일만` 잘 하도록 구현한다.
- `else를 지양`한다
- if 조건절에서 값을 return하는 방식으로 구현하면 else를 사용하지 않아도 된다
- `도메인 로직에 단위 테스트`를 구현해야 한다.
- 단 UI(Console.readLineAsync, Console.print) 로직에 대한 단위 테스트는 제외한다.
- 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다.

---

<br>

## 📢 2주차 공통 피드백 📢

- `README. md`를 상세히 작성한다
- 기능 목록을 `재검토`한다
- 에외 상황도 정리한다
- 기능 목록을 `업데이트`한다
- 값을 `하드 코딩`하지 않는다
- 상수를 만들고 이름을 부여해 변수의 역할이 무엇인지 의도를 드러낸다
- `구현 순서`도 코딩 컨벤션이다
- 클래스는 필드, 생성자, 메서드 순으로 작성한다
- `한 함수가 한 가지 기능`만 담당하게 한다
- 함수가 한 가지 기능을 하는지 `확인하는 기준`을 세운다
- 함수의 길이를 15라인이 넘어가지 않도록 구현한다
- JavaScript에서 `객체를 만드는 다양한 방법`을 이해하고 사용한다
- `테스트를 작성하는 이유`에 대해 본인의 경험을 토대로 정리해본다
- 처음부터 큰 단위의 테스트를 만들지 않는다

---

<br>

## 📢 1주차 공통 피드백 📢

- `요구사항을 정확히 준수`한다
- `커밋 메시지`를 의미 있게 작성한다
- `git`을 통해 관리할 자원에 대해서도 고려한다
- Pull Request를 보내기 전 `브랜치를 확인`한다
- PR을 한 번 작성했다면 닫지 말고 `추가 커밋`을 한다
- `이름을 통해 의도`를 드러낸다
- `축약`하지 않는다
- `공백`도 코딩 컨벤션이다
- `공백 라인`을 의미 있게 사용한다
- `space와 tab`을 혼용하지 않는다
- `의미 없는 주석`을 달지 않는다
- `linter`와 `Code Formatter`의 기능을 활용한다
- `EOL(End Of Line)`
- `불필요한 console.log`를 남기지 않는다
- `JavaScript에서 제공하는 API`를 적극 활용한다

---
Loading