diff --git a/README.md b/README.md index 5fa2560b46..b90b672af2 100644 --- a/README.md +++ b/README.md @@ -1 +1,244 @@ -# java-lotto-precourse +# ๐ŸŽฐ ๋กœ๋˜ ๊ฒŒ์ž„ (Lotto Game) + +์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๊ธˆ์•ก๋งŒํผ ๋กœ๋˜๋ฅผ ์ž๋™์œผ๋กœ ๋ฐœํ–‰ํ•˜๊ณ , ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ฒฐ๊ณผ ํ†ต๊ณ„์™€ ์ˆ˜์ต๋ฅ ์„ ์ถœ๋ ฅํ•˜๋Š” ์ฝ˜์†” ๊ธฐ๋ฐ˜ ํ”„๋กœ๊ทธ๋žจ์ž…๋‹ˆ๋‹ค. + +Java 21 ํ™˜๊ฒฝ์—์„œ ๋™์ž‘ํ•˜๋ฉฐ, ๊ธฐ๋Šฅ๋ณ„ ์ฑ…์ž„์„ ๋ถ„๋ฆฌํ•ด ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ์ค‘์ ์„ ๋‘์—ˆ์Šต๋‹ˆ๋‹ค. + +
+
+ +## ๐Ÿญ **๐ŸŽฏ ํ”„๋กœ์ ํŠธ์˜ ์ง€ํ–ฅ์  ๋ฐ** ๋ฐฉํ–ฅ + +๊ด€๋ จ๋œ ๋ฐ์ดํ„ฐ์™€ ๋™์ž‘์„ ํ•จ๊ป˜ ๋ฌถ์–ด **๊ฐ ํด๋ž˜์Šค๊ฐ€ ๋ถ„๋ช…ํ•œ ์ฑ…์ž„์„ ๊ฐ–๋„๋ก ๊ตฌ์„ฑ**ํ•˜๊ณ , **์—ฌ๋Ÿฌ ๊ฐ์ฒด๋“ค์ด ์—ญํ• ์— ๋”ฐ๋ผ ํ˜‘๋ ฅ**ํ•˜๋ฉฐ ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ์„ ์™„์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„ํ•ฉ๋‹ˆ๋‹ค. + +TDD ์‚ฌ์ดํด ์ค‘ **Inside-Out ๋ฐฉ์‹์„ ๊ธฐ๋ฐ˜์œผ๋กœ**, **์ž…์ถœ๋ ฅ๋ณด๋‹ค๋Š” ๋จผ์ € ๋„๋ฉ”์ธ ๋กœ์ง์„ ์ค‘์‹ฌ์œผ๋กœ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘**ํ•˜๊ณ , ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•œ ์ž‘์€ ๋‹จ์œ„๋ถ€ํ„ฐ ์ ์ฐจ ํ™•์žฅํ•ด ๋‚˜๊ฐ‘๋‹ˆ๋‹ค. + +
+
+ +## ๐Ÿ” ์‹œํ€€์Šค ๋‹ค์ด์–ด๊ทธ๋žจ + +์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ „์ฒด ๋™์ž‘ ๊ณผ์ •๊ณผ ๊ฐ์ฒด ๊ฐ„์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ์‹œ๊ฐํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์‹œํ€€์Šค ๋‹ค์ด์–ด๊ทธ๋žจ์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. + +```mermaid +sequenceDiagram + participant U as ์‚ฌ์šฉ์ž + participant I as InputView + participant O as OutputView + participant C as LottoController + participant M as Money + participant F as LottoFactory + participant G as LottoGenerator + participant L as Lotto + participant W as WinningNumbers + participant R as LottoResults + +Note over U,C: ๐ŸŽซ 1๋‹จ๊ณ„: ๋กœ๋˜ ๊ตฌ๋งค + loop ์œ ํšจํ•œ ๊ธˆ์•ก์ด ์ž…๋ ฅ๋  ๋•Œ๊นŒ์ง€ + U->>I: ๊ตฌ์ž… ๊ธˆ์•ก ์ž…๋ ฅ + I->>C: ๊ธˆ์•ก ์ „๋‹ฌ + C->>M: Money ์ƒ์„ฑ ๋ฐ ๊ฒ€์ฆ + alt ๊ฒ€์ฆ ์„ฑ๊ณต + M-->>C: ์œ ํšจํ•œ Money ๊ฐ์ฒด + else ๊ฒ€์ฆ ์‹คํŒจ + M-->>C: IllegalArgumentException + C->>O: ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ + O->>U: ์—๋Ÿฌ ํ‘œ์‹œ + end + end + + C->>M: getLottoCount() ํ˜ธ์ถœ + M-->>C: ๊ตฌ๋งค ๊ฐ€๋Šฅํ•œ ๋กœ๋˜ ๊ฐœ์ˆ˜(count) + + C->>F: generate(count) ํ˜ธ์ถœ + F->>G: generate(count) ์œ„์ž„ + + loop count๋ฒˆ + G->>G: Randoms.pickUniqueNumbersInRange(1,45,6) + G->>L: Lotto(numbers) ์ƒ์„ฑ + end + G-->>F: List ๋ฐ˜ํ™˜ + F-->>C: List ๋ฐ˜ํ™˜ + + C->>O: ๊ตฌ๋งคํ•œ ๋กœ๋˜ ๋ชฉ๋ก ์ถœ๋ ฅ + O->>U: ๋กœ๋˜ ๋ชฉ๋ก ํ‘œ์‹œ + +Note over U,C: ๐Ÿ† 2๋‹จ๊ณ„: ๋‹น์ฒจ ๋ฒˆํ˜ธ ์ž…๋ ฅ + loop ์œ ํšจํ•œ ๋‹น์ฒจ ๋ฒˆํ˜ธ๊ฐ€ ์ž…๋ ฅ๋  ๋•Œ๊นŒ์ง€ + U->>I: ๋‹น์ฒจ ๋ฒˆํ˜ธ ์ž…๋ ฅ (์‰ผํ‘œ ๊ตฌ๋ถ„) + I->>C: ๋‹น์ฒจ ๋ฒˆํ˜ธ ์ „๋‹ฌ + C->>W: WinningNumbers ์ƒ์„ฑ ๋ฐ ๊ฒ€์ฆ + alt ๊ฒ€์ฆ ์„ฑ๊ณต + W-->>C: ๋‹น์ฒจ ๋ฒˆํ˜ธ ๊ฐ์ฒด ์ƒ์„ฑ๋จ + else ๊ฒ€์ฆ ์‹คํŒจ + W-->>C: IllegalArgumentException + C->>O: ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ + O->>U: ์—๋Ÿฌ ํ‘œ์‹œ + end + end + + loop ์œ ํšจํ•œ ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๊ฐ€ ์ž…๋ ฅ๋  ๋•Œ๊นŒ์ง€ + U->>I: ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ ์ž…๋ ฅ + I->>C: ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ ์ „๋‹ฌ + C->>W: ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ ์„ค์ • ๋ฐ ๊ฒ€์ฆ + alt ๊ฒ€์ฆ ์„ฑ๊ณต + W-->>C: ์ตœ์ข… ๋‹น์ฒจ ๋ฒˆํ˜ธ ๊ฐ์ฒด ์™„์„ฑ + else ๊ฒ€์ฆ ์‹คํŒจ + W-->>C: IllegalArgumentException + C->>O: ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ + O->>U: ์—๋Ÿฌ ํ‘œ์‹œ + end + end + Note over U,C: ๐Ÿ“Š 3๋‹จ๊ณ„: ๊ฒฐ๊ณผ ๋ถ„์„ ๋ฐ ์ถœ๋ ฅ + C->>R: LottoResults ์ƒ์„ฑ (List, WinningNumbers) + loop ๊ฐ ๋กœ๋˜์— ๋Œ€ํ•ด + R->>R: ๋“ฑ์ˆ˜๋ณ„ ์นด์šดํŠธ ์ฆ๊ฐ€ + R->>R: ์ด ์ƒ๊ธˆ ๋ˆ„์  + end + C->>R: calculateYield(๊ตฌ์ž… ๊ธˆ์•ก) ํ˜ธ์ถœ + R-->>C: ์ˆ˜์ต๋ฅ  ๋ฐ˜ํ™˜ + C->>O: ๋‹น์ฒจ ํ†ต๊ณ„ ์ถœ๋ ฅ + O->>U: ํ†ต๊ณ„ ํ‘œ์‹œ + C->>O: ์ˆ˜์ต๋ฅ  ์ถœ๋ ฅ + O->>U: ์ˆ˜์ต๋ฅ  ํ‘œ์‹œ +``` + +
+
+ +## **๐Ÿงฑ ๊ฐ์ฒด ์—ญํ•  ์š”์•ฝ** + +| ๊ฐ์ฒด ์ด๋ฆ„ | ์—ญํ• (์ฑ…์ž„) | ๋ณด์œ  ๊ฐ’(์ƒํƒœ) | ์ฃผ์š” ํ–‰์œ„ | +|------------------------|---------------------------------------------|---------------------------------|------------------------------------------------------------------| +| `Money` | ๊ตฌ์ž… ๊ธˆ์•ก์„ ํ‘œํ˜„ํ•˜๊ณ  ๊ด€๋ จ ๊ทœ์น™์„ ๊ฒ€์ฆํ•˜๋Š” ๊ฐ’ ๊ฐ์ฒด | * ๊ตฌ์ž… ๊ธˆ์•ก | - ๊ธˆ์•ก ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ, ๊ตฌ๋งค ๊ฐ€๋Šฅํ•œ ๋กœ๋˜ ์ˆ˜ ๊ณ„์‚ฐ | +| `Lotto` | ๋กœ๋˜ ํ•œ ์žฅ์„ ํ‘œํ˜„ํ•˜๋ฉฐ ๋ฒˆํ˜ธ ๊ทœ์น™(๊ฐœ์ˆ˜, ๋ฒ”์œ„, ์ค‘๋ณต)์˜ ๋ถˆ๋ณ€์„ฑ์„ ๋ณด์žฅ | * 6๊ฐœ์˜ ๋กœ๋˜ ๋ฒˆํ˜ธ | - ๋ฒˆํ˜ธ ์ •๋ ฌ ๋ฐ ์ค‘๋ณต ๊ฒ€์‚ฌ, ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ์ผ์น˜ ๊ฐœ์ˆ˜ ๊ณ„์‚ฐ, ๋ณด๋„ˆ์Šค ํฌํ•จ ์—ฌ๋ถ€ ํ™•์ธ | +| `LottoGenerator` | ๋กœ๋˜ ๋ฒˆํ˜ธ ์ƒ์„ฑ ์ „๋žต์„ ์ถ”์ƒํ™”ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค | | - ์š”์ฒญ๋œ ๊ฐœ์ˆ˜๋งŒํผ `Lotto` ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ | +| `RandomLottoGenerator` | ๋žœ๋ค ์ „๋žต์œผ๋กœ ๋กœ๋˜ ๊ทœ์น™์— ๋งž๋Š” ๋ฒˆํ˜ธ๋ฅผ ์ƒ์„ฑ | | - `Randoms.pickUniqueNumbersInRange()`๋ฅผ ์‚ฌ์šฉํ•ด ๋ฒˆํ˜ธ ์ƒ์„ฑ, `Lotto` ๊ฐ์ฒด ์ƒ์„ฑ | +| `LottoFactory` | ๋กœ๋˜ ์ƒ์„ฑ ์ „๋žต(`LottoGenerator`)์— ์ƒ์„ฑ ์š”์ฒญ์„ ์œ„์ž„ํ•˜๋Š” ํŒฉํ† ๋ฆฌ | * `LottoGenerator` ๊ตฌํ˜„์ฒด | - ์ƒ์„ฑ๊ธฐ null ๊ฒ€์ฆ, ์ƒ์„ฑ ๊ฐœ์ˆ˜ ๊ฒ€์ฆ, ์ƒ์„ฑ๊ธฐ์— ๋กœ๋˜ ์ƒ์„ฑ ์œ„์ž„ | +| `WinningNumbers` | ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋ฅผ ๊ฐ€์ง€๋ฉฐ ๊ด€๋ จ ๊ทœ์น™(๊ฐœ์ˆ˜, ๋ฒ”์œ„, ์ค‘๋ณต)์„ ์ฑ…์ž„์ง | * 6๊ฐœ ๋‹น์ฒจ ๋ฒˆํ˜ธ, 1๊ฐœ ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ | - ๋‹น์ฒจ ๋ฒˆํ˜ธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ, ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋ฐ ์ค‘๋ณต ํ™•์ธ | +| `LottoRank` | ๋‹น์ฒจ ๋“ฑ์ˆ˜(1~5๋“ฑ, ๊ฝ)์˜ ์กฐ๊ฑด(์ผ์น˜ ๊ฐœ์ˆ˜, ๋ณด๋„ˆ์Šค)๊ณผ ์ƒ๊ธˆ์„ ์ •์˜ | * Enum(์ผ์น˜ ๊ฐœ์ˆ˜, ๋ณด๋„ˆ์Šค ํฌํ•จ ์—ฌ๋ถ€, ๋‹น์ฒจ ๊ธˆ์•ก) | - ์ผ์น˜ ์ˆ˜/๋ณด๋„ˆ์Šค ํฌํ•จ ์—ฌ๋ถ€๋กœ ๋“ฑ์ˆ˜ ๋ฐ˜ํ™˜, ์ƒ๊ธˆ ๋ฐ˜ํ™˜ | +| `LottoResults` | ์ „์ฒด ๋‹น์ฒจ ๊ฒฐ๊ณผ๋ฅผ ์ง‘๊ณ„ํ•˜๊ณ  ์ตœ์ข… ์ˆ˜์ต๋ฅ ์„ ๊ณ„์‚ฐ | * ๋“ฑ์ˆ˜๋ณ„ ๋‹น์ฒจ ํšŸ์ˆ˜, ๋ˆ„์  ์ƒ๊ธˆ | - ๊ฐ ๋กœ๋˜์— ๋Œ€ํ•œ ๋“ฑ์ˆ˜ ๊ณ„์‚ฐ, ๋‹น์ฒจ ๊ฒฐ๊ณผ ๋ˆ„์ , ์ˆ˜์ต๋ฅ  ๊ณ„์‚ฐ | +| `InputView` | ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ์ž…๋ ฅ์„ ๋ฐ›๋Š” ๋ชจ๋“  ๋กœ์ง์„ ๋‹ด๋‹น | * ์ž…๋ ฅ ์ ‘๋‘์‚ฌ | - ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ์ž…๋ ฅ์„ ๋ฐ›์Œ, `Console.readLine()` ์‚ฌ์šฉ | +| `OutputView` | ์‚ฌ์šฉ์ž์—๊ฒŒ ์ •๋ณด๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๋ชจ๋“  ๋กœ์ง์„ ๋‹ด๋‹น | * ์ถœ๋ ฅ ์ ‘๋‘์‚ฌ | - ๊ตฌ๋งคํ•œ ๋กœ๋˜ ๋ชฉ๋ก ์ถœ๋ ฅ, ๋‹น์ฒจ ๊ฒฐ๊ณผ ์ถœ๋ ฅ, ์ˆ˜์ต๋ฅ  ์ถœ๋ ฅ | +| `InputValidator` | ๋ฌธ์ž์—ด ์ž…๋ ฅ์„ ์ˆซ์ž/๋ฆฌ์ŠคํŠธ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  ํ˜•์‹์„ ๊ฒ€์ฆํ•˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ | - | - ๊ตฌ์ž… ๊ธˆ์•ก/๋‹น์ฒจ ๋ฒˆํ˜ธ/๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ ๋ฌธ์ž์—ด ํŒŒ์‹ฑ, ๊ณต๋ฐฑ ๋ฐ ์ˆซ์ž ํ˜•์‹ ๊ฒ€์ฆ | +| `LottoController` | ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ „์ฒด ์‹คํ–‰ ํ๋ฆ„์„ ์กฐ์œจ. View์™€ Domain ๊ฐ์ฒด๋“ค์„ ์—ฐ๊ฒฐ | | - ์ „์ฒด ํ๋ฆ„ ์ œ์–ด | + +
+
+ +## **๐Ÿšฐ ๊ตฌํ˜„ ์ˆœ์„œ (Inside-Out ๋ฐฉ์‹)** + +### **1๋‹จ๊ณ„: ํ•ต์‹ฌ ๋„๋ฉ”์ธ ๋ชจ๋ธ ๊ตฌํ˜„** + +> ๋น„์ฆˆ๋‹ˆ์Šค์˜ ๊ฐ€์žฅ ๋ณธ์งˆ์ ์ธ ๊ทœ์น™๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ์ •์˜ํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค. +> + +- `Money`: ๊ธˆ์•ก์„ ํ‘œํ˜„ํ•˜๊ณ , 1,000์› ๋‹จ์œ„ ๊ฒ€์ฆ ๋ฐ ๋กœ๋˜ ๊ตฌ๋งค ๊ฐ€๋Šฅ ๊ฐœ์ˆ˜ ๊ณ„์‚ฐ ์ฑ…์ž„์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค. +- `Lotto`: ๋กœ๋˜ ํ•œ ์žฅ(6๊ฐœ ๋ฒˆํ˜ธ)์„ ํ‘œํ˜„ํ•˜๋ฉฐ, ๋ฒˆํ˜ธ ๊ฐœ์ˆ˜, ๋ฒ”์œ„, ์ค‘๋ณต ๋“ฑ ๊ทœ์น™์˜ ๋ถˆ๋ณ€์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. + +### **2๋‹จ๊ณ„: ๋‹น์ฒจ ๊ทœ์น™ ๋ฐ ํŒ์ • ๋กœ์ง ๊ตฌํ˜„** + +> ๋‹น์ฒจ์„ ๊ฒฐ์ •ํ•˜๋Š” ๊ธฐ์ค€๊ณผ ๋กœ์ง์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. +> + +- `WinningNumbers`: ๋‹น์ฒจ ๋ฒˆํ˜ธ(6๊ฐœ)์™€ ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ(1๊ฐœ)๋ฅผ ๊ฐ€์ง€๋ฉฐ, ๊ด€๋ จ๋œ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ์ฑ…์ž„์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค. +- `LottoRank`: 5๊ฐœ ๋“ฑ์ˆ˜์™€ '๊ฝ'์— ๋Œ€ํ•œ ์กฐ๊ฑด(์ผ์น˜ ๊ฐœ์ˆ˜, ๋ณด๋„ˆ์Šค ์—ฌ๋ถ€)๊ณผ ์ƒ๊ธˆ์„ `enum`์œผ๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. **(๋“ฑ์ˆ˜ ํŒ์ • ๋กœ์ง ํฌํ•จ)** + +### **3๋‹จ๊ณ„: ๊ฒฐ๊ณผ ์ง‘๊ณ„ ๋ฐ ๋ถ„์„ ๋กœ์ง ๊ตฌํ˜„** + +> ๊ตฌ๋งคํ•œ ๋กœ๋˜๋“ค์˜ ๊ฒฐ๊ณผ๋ฅผ ์ข…ํ•ฉํ•˜์—ฌ ํ†ต๊ณ„๋ฅผ ๋‚ด๋Š” ์ฑ…์ž„์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. +> + +- `LottoResults`: ๊ตฌ๋งคํ•œ ๋ชจ๋“  `Lotto`์™€ `WinningNumbers`๋ฅผ ๋น„๊ตํ•˜์—ฌ, ๋“ฑ์ˆ˜๋ณ„ ๋‹น์ฒจ ํšŸ์ˆ˜๋ฅผ ์ง‘๊ณ„ํ•˜๊ณ  ์ตœ์ข… ์ˆ˜์ต๋ฅ ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. + +### **4๋‹จ๊ณ„: ๋กœ๋˜ ์ƒ์„ฑ ๋กœ์ง ๊ตฌํ˜„** + +> ๋„๋ฉ”์ธ ๊ทœ์น™์— ๋”ฐ๋ผ ๋กœ๋˜๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ฑ…์ž„์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. +> + +- `LottoFactory`: `LottoGenerator`๋ฅผ ์‚ฌ์šฉํ•ด ๊ทœ์น™์— ๋งž๋Š” ๋กœ๋˜ ๋ชฉ๋ก์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. + +### **5๋‹จ๊ณ„: ์ž…์ถœ๋ ฅ(UI) ๊ณ„์ธต ๊ตฌํ˜„** + +> ์‚ฌ์šฉ์ž์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๋ถ€๋ถ„์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. +> + +- `InputView`: ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ๊ตฌ์ž… ๊ธˆ์•ก, ๋‹น์ฒจ ๋ฒˆํ˜ธ, ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅ๋ฐ›๋Š” ์ฑ…์ž„์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค. +- `OutputView`: ๊ตฌ๋งค ๋‚ด์—ญ, ๋‹น์ฒจ ํ†ต๊ณ„, ์ˆ˜์ต๋ฅ , ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๋“ฑ ๋ชจ๋“  ์ถœ๋ ฅ ๋‚ด์šฉ์„ ํ˜•์‹์— ๋งž๊ฒŒ ๋ณด์—ฌ์ฃผ๋Š” ์ฑ…์ž„์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค. +- `InputValidator`: `InputView`๋ฅผ ํ†ตํ•ด ๋“ค์–ด์˜จ ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๊ฐ’์˜ ํ˜•์‹(์ˆซ์ž์ธ์ง€, ์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„๋˜๋Š”์ง€ ๋“ฑ)์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. + +### **6๋‹จ๊ณ„: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ†ตํ•ฉ ๋ฐ ์‹คํ–‰** + +> ๊ตฌํ˜„๋œ ๋ชจ๋“  ๊ฐ์ฒด๋ฅผ ์กฐ๋ฆฝํ•˜์—ฌ ํ”„๋กœ๊ทธ๋žจ์„ ์™„์„ฑํ•ฉ๋‹ˆ๋‹ค. +> + +- `LottoController`: ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‹คํ–‰ ํ๋ฆ„(๋กœ๋˜ ๊ตฌ๋งค โ†’ ๋‹น์ฒจ ๋ฒˆํ˜ธ ์ž…๋ ฅ โ†’ ๊ฒฐ๊ณผ ๋ฐœํ‘œ)์„ ์ œ์–ดํ•˜๊ณ , ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ์žฌ์ž…๋ ฅ ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. +- `Application`: ํ”„๋กœ๊ทธ๋žจ์˜ ์‹œ์ž‘์ (`main` ๋ฉ”์„œ๋“œ)์œผ๋กœ, `LottoController`๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. + +### **7๋‹จ๊ณ„: ๋ฆฌํŒฉํ† ๋ง ๋ฐ ์ตœ์ข… ๊ฒ€ํ† ** + +> ์ฝ”๋“œ ํ’ˆ์งˆ์„ ๊ฐœ์„ ํ•˜๊ณ  ์š”๊ตฌ์‚ฌํ•ญ์„ ์ตœ์ข… ์ ๊ฒ€ํ•ฉ๋‹ˆ๋‹ค. +> + +- `ValidationUtil`, `Retry` ๋“ฑ ์ค‘๋ณต ๋กœ์ง์„ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋กœ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค. +- ์ „์ฒด ์ฝ”๋“œ์— ๋Œ€ํ•ด ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ œ์•ฝ ์กฐ๊ฑด(๋“ค์—ฌ์“ฐ๊ธฐ, ๋ฉ”์„œ๋“œ ๊ธธ์ด ๋“ฑ) ์ค€์ˆ˜ ์—ฌ๋ถ€๋ฅผ ๊ฒ€ํ† ํ•ฉ๋‹ˆ๋‹ค. + +
+
+ +## ๐Ÿ“‹ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ + +์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ ์˜ค๋ฅ˜๋กœ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด **`[ERROR] ...` ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ถœ๋ ฅํ•˜๊ณ  ํ•ด๋‹น ์ž…๋ ฅ๋ถ€ํ„ฐ ๋‹ค์‹œ ์ž…๋ ฅ์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.** + +| ์˜ˆ์™ธ ์ƒํ™ฉ | ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ | +|----------------------------|-------------------------------------------| +| ๋‹น์ฒจ ๊ฒฐ๊ณผ๋ฅผ ๊ณ„์‚ฐํ•  ๋กœ๋˜ ๋ชฉ๋ก์ด null์ธ ๊ฒฝ์šฐ | `[ERROR] ๋‹น์ฒจ ๊ฒฐ๊ณผ๋ฅผ ๊ณ„์‚ฐํ•  ๋กœ๋˜ ๋ชฉ๋ก์€ null์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.` | +| ๋กœ๋˜ ๋ชฉ๋ก์— null์ด ํฌํ•จ๋œ ๊ฒฝ์šฐ | `[ERROR] ๋กœ๋˜ ๋ชฉ๋ก์— null์ด ํฌํ•จ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.` | +| ๋‹น์ฒจ ๋ฒˆํ˜ธ๊ฐ€ null์ธ ๊ฒฝ์šฐ | `[ERROR] ๋‹น์ฒจ ๋ฒˆํ˜ธ๋Š” null์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.` | +| ์ˆ˜์ต๋ฅ  ๊ณ„์‚ฐ์‹œ ๊ตฌ์ž…๊ธˆ์•ก์ด 0 ์ดํ•˜์ธ ๊ฒฝ์šฐ | `[ERROR] ๊ตฌ์ž… ๊ธˆ์•ก์€ 0๋ณด๋‹ค ์ปค์•ผ ํ•ฉ๋‹ˆ๋‹ค.` | +| ๊ตฌ์ž… ๊ธˆ์•ก์ด 0 ์ดํ•˜์ธ ๊ฒฝ์šฐ | `[ERROR] ๊ตฌ์ž… ๊ธˆ์•ก์€ 0๋ณด๋‹ค ์ปค์•ผ ํ•ฉ๋‹ˆ๋‹ค.` | +| ๊ตฌ์ž… ๊ธˆ์•ก์ด 1,000์› ๋‹จ์œ„๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ | `[ERROR] 1,000์› ๋‹จ์œ„๋กœ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.` | +| ๋กœ๋˜ ๋ฒˆํ˜ธ ๋ฆฌ์ŠคํŠธ๊ฐ€ null์ธ ๊ฒฝ์šฐ | `[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” null์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.` | +| ๋น„๊ตํ•  ๋กœ๋˜ ๋ฒˆํ˜ธ ๋ฆฌ์ŠคํŠธ๊ฐ€ null์ธ ๊ฒฝ์šฐ | `[ERROR] ๋น„๊ตํ•  ๋กœ๋˜๊ฐ€ null์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.` | +| ๋กœ๋˜ ๋ฒˆํ˜ธ๊ฐ€ 6๊ฐœ๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ | `[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 6๊ฐœ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.` | +| ๋กœ๋˜ ๋ฒˆํ˜ธ์— ์ค‘๋ณต์ด ์žˆ๋Š” ๊ฒฝ์šฐ | `[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” ์ค‘๋ณต๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.` | +| ๋กœ๋˜ ๋ฒˆํ˜ธ๊ฐ€ 1~45 ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋Š” ๊ฒฝ์šฐ | `[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 1๋ถ€ํ„ฐ 45 ์‚ฌ์ด์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.` | +| ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๊ฐ€ 1~45 ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚œ ๊ฒฝ์šฐ | `[ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” 1๋ถ€ํ„ฐ 45 ์‚ฌ์ด์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.` | +| ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๊ฐ€ ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ์ค‘๋ณต๋˜๋Š” ๊ฒฝ์šฐ | `[ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ์ค‘๋ณต๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.` | +| ๋กœ๋˜ ์ƒ์„ฑ๊ธฐ๊ฐ€ null์ธ ๊ฒฝ์šฐ | `[ERROR] ๋กœ๋˜ ์ƒ์„ฑ๊ธฐ๋Š” null์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.` | +| ๋กœ๋˜ ์ƒ์„ฑ ๊ฐœ์ˆ˜๊ฐ€ 0 ์ดํ•˜์ธ ๊ฒฝ์šฐ | `[ERROR] ๋กœ๋˜ ์ƒ์„ฑ ๊ฐœ์ˆ˜๋Š” 1๊ฐœ ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.` | +| ์ž…๋ ฅ ๋ฌธ์ž์—ด์ด null ๋˜๋Š” ๊ณต๋ฐฑ์ธ ๊ฒฝ์šฐ | `[ERROR] ์ž…๋ ฅ์€ ๋น„์–ด ์žˆ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.` | +| ๊ตฌ์ž… ๊ธˆ์•ก ์ž…๋ ฅ์ด ์ˆซ์ž๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ | `[ERROR] ๊ตฌ์ž… ๊ธˆ์•ก์€ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.` | +| ๋‹น์ฒจ ๋ฒˆํ˜ธ ์ž…๋ ฅ์— ์ˆซ์ž๊ฐ€ ์•„๋‹Œ ๊ฐ’์ด ํฌํ•จ๋œ ๊ฒฝ์šฐ | `[ERROR] ๋‹น์ฒจ ๋ฒˆํ˜ธ๋Š” ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.` | +| ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ ์ž…๋ ฅ์ด ์ˆซ์ž๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ | `[ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.` | + +
+
+ +## โš™๏ธ ๊ฐœ๋ฐœ ๋ฐ ๊ธฐ์ˆ  ์ œ์•ฝ + +- ์ด ํ”„๋กœ๊ทธ๋žจ์€ **Java 21**์—์„œ ์‹คํ–‰๋˜์–ด์•ผ ํ•œ๋‹ค. +- ํ”„๋กœ๊ทธ๋žจ์˜ ์‹œ์ž‘์ ์€ **`Application` ํด๋ž˜์Šค์˜ `main()`** ๋ฉ”์„œ๋“œ์—ฌ์•ผ ํ•œ๋‹ค. +- **์ œ๊ณต๋œ `build.gradle` ํŒŒ์ผ์€ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๋‹ค.** ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๋Š”๋‹ค. +- ๋กœ๋˜ ๋ฒˆํ˜ธ ์ƒ์„ฑ์€ **`camp.nextstep.edu.missionutils.Randoms.pickUniqueNumbersInRange(1, 45, 6)`** ์„ ์‚ฌ์šฉํ•œ๋‹ค. +- ์‚ฌ์šฉ์ž ์ž…๋ ฅ์€ **`camp.nextstep.edu.missionutils.Console.readLine()`** ์œผ๋กœ๋งŒ ๋ฐ›๋Š”๋‹ค. +- ํ”„๋กœ๊ทธ๋žจ ์ข…๋ฃŒ๋ฅผ ์œ„ํ•ด **`System.exit()`๋ฅผ ํ˜ธ์ถœํ•ด์„œ๋Š” ์•ˆ ๋œ๋‹ค.** +- ๋“ค์—ฌ์“ฐ๊ธฐ๋Š” **2๋‹จ๊ณ„ ์ด๋‚ด**๋กœ ์œ ์ง€ํ•œ๋‹ค. +- **`else` ๋ฌธ, `switch/case`, ์‚ผํ•ญ ์—ฐ์‚ฐ์ž**๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. +- ํ•˜๋‚˜์˜ ๋ฉ”์„œ๋“œ๋Š” **ํ•œ ๊ฐ€์ง€ ์ผ๋งŒ** ํ•˜๋„๋ก ์ž‘์„ฑํ•˜๊ณ , **15์ค„์„ ๋„˜๊ธฐ์ง€ ์•Š๋„๋ก** ํ•œ๋‹ค. +- ์ œ๊ณต๋œ `Lotto` ํด๋ž˜์Šค์—๋Š” **ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๋Š”๋‹ค.** + +
+
+ +## ๐Ÿ“ ๊ตฌํ˜„ ๊ธฐ๋ก / ๊ณ ๋ฏผ ์ •๋ณด + +๊ตฌํ˜„ ๊ณผ์ •์—์„œ ๋ฐฐ์šฐ๊ณ  ๊ณ ๋ฏผํ–ˆ๋˜ ๊ฒƒ๋“ค์„ ๊ธฐ๋กํ•˜๋Š” ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค. + +- [๋„๋ฉ”์ธ ์„ค๊ณ„์‹œ ๊ณ ๋ คํ–ˆ๋˜ ๊ฒƒ](https://github.com/JohnPrk/java-lotto-8/issues/1) +- [Inside-Out vs Outside-In TDD ์„ ํƒ ์ด์œ ](https://github.com/JohnPrk/java-lotto-8/issues/2) +- [Supplier๋Š” ๋ฌด์—‡์ธ๊ฐ€?](https://github.com/JohnPrk/java-lotto-8/issues/3) +- [2023๋…„ ๊ตฌํ˜„ vs ์ง€๊ธˆ ๊ตฌํ˜„ ๋น„๊ต[ํšŒ๊ณ ]](https://github.com/JohnPrk/java-lotto-8/issues/4) + +
+
diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index d190922ba4..14e77be8a8 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,7 +1,14 @@ package lotto; +import lotto.controller.LottoController; +import lotto.domain.LottoFactory; +import lotto.domain.RandomLottoGenerator; + public class Application { + public static void main(String[] args) { - // TODO: ํ”„๋กœ๊ทธ๋žจ ๊ตฌํ˜„ + LottoFactory lottoFactory = new LottoFactory(new RandomLottoGenerator()); + LottoController controller = new LottoController(lottoFactory); + controller.run(); } } diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java deleted file mode 100644 index 88fc5cf12b..0000000000 --- a/src/main/java/lotto/Lotto.java +++ /dev/null @@ -1,20 +0,0 @@ -package lotto; - -import java.util.List; - -public class Lotto { - private final List numbers; - - public Lotto(List numbers) { - validate(numbers); - this.numbers = numbers; - } - - private void validate(List numbers) { - if (numbers.size() != 6) { - throw new IllegalArgumentException("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 6๊ฐœ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); - } - } - - // TODO: ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„ -} diff --git a/src/main/java/lotto/controller/LottoController.java b/src/main/java/lotto/controller/LottoController.java new file mode 100644 index 0000000000..5864bd78c5 --- /dev/null +++ b/src/main/java/lotto/controller/LottoController.java @@ -0,0 +1,71 @@ +package lotto.controller; + +import lotto.domain.*; +import lotto.view.InputView; +import lotto.view.OutputView; + +import java.util.List; + +public class LottoController { + + private final LottoFactory lottoFactory; + + public LottoController(LottoFactory lottoFactory) { + this.lottoFactory = lottoFactory; + } + + public void run() { + Money money = readMoney(); + int lottoCount = money.getLottoCount(); + List lottos = lottoFactory.generate(lottoCount); + OutputView.printLottos(lottos); + WinningNumbers winningNumbers = readWinningNumbers(); + LottoResults results = new LottoResults(lottos, winningNumbers); + OutputView.printStatistics(results); + double yield = results.calculateYield(money.getAmount()); + OutputView.printYield(yield); + } + + private Money readMoney() { + while (true) { + try { + int purchaseAmount = InputView.readPurchaseAmount(); + return new Money(purchaseAmount); + } catch (IllegalArgumentException e) { + OutputView.printError(e.getMessage()); + } + } + } + + private WinningNumbers readWinningNumbers() { + while (true) { + try { + List numbers = readWinningNumberList(); + int bonus = readBonusNumber(); + return new WinningNumbers(numbers, bonus); + } catch (IllegalArgumentException e) { + OutputView.printError(e.getMessage()); + } + } + } + + private List readWinningNumberList() { + while (true) { + try { + return InputView.readWinningNumbers(); + } catch (IllegalArgumentException e) { + OutputView.printError(e.getMessage()); + } + } + } + + private int readBonusNumber() { + while (true) { + try { + return InputView.readBonusNumber(); + } catch (IllegalArgumentException e) { + OutputView.printError(e.getMessage()); + } + } + } +} diff --git a/src/main/java/lotto/domain/Lotto.java b/src/main/java/lotto/domain/Lotto.java new file mode 100644 index 0000000000..18bdc0ef86 --- /dev/null +++ b/src/main/java/lotto/domain/Lotto.java @@ -0,0 +1,78 @@ +package lotto.domain; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class Lotto { + + private static final int LOTTO_SIZE = 6; + private static final int MIN_NUMBER = 1; + private static final int MAX_NUMBER = 45; + private final List numbers; + + public Lotto(List numbers) { + validateNotNull(numbers); + validateCount(numbers); + validateRange(numbers); + validateDuplicate(numbers); + this.numbers = sortNumbers(numbers); + } + + public List getNumbers() { + return Collections.unmodifiableList(numbers); + } + + public boolean containsNumber(int number) { + return numbers.contains(number); + } + + public int countMatchingNumbers(Lotto otherLotto) { + validateOtherLottoNotNull(otherLotto); + Set otherNumbers = new HashSet<>(otherLotto.numbers); + return (int) numbers.stream() + .filter(otherNumbers::contains) + .count(); + } + + private void validateOtherLottoNotNull(Lotto otherLotto) { + if (otherLotto == null) { + throw new IllegalArgumentException("[ERROR] ๋น„๊ตํ•  ๋กœ๋˜๊ฐ€ null์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + } + + private void validateNotNull(List numbers) { + if (numbers == null) { + throw new IllegalArgumentException("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” null์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + } + + private void validateCount(List numbers) { + if (numbers.size() != LOTTO_SIZE) { + throw new IllegalArgumentException("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 6๊ฐœ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + } + + private void validateRange(List numbers) { + for (int number : numbers) { + if (number < MIN_NUMBER || number > MAX_NUMBER) { + throw new IllegalArgumentException("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 1๋ถ€ํ„ฐ 45 ์‚ฌ์ด์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + } + } + + private void validateDuplicate(List numbers) { + Set uniqueNumbers = new HashSet<>(numbers); + if (uniqueNumbers.size() != LOTTO_SIZE) { + throw new IllegalArgumentException("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” ์ค‘๋ณต๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + } + + private List sortNumbers(List numbers) { + return numbers.stream() + .sorted() + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/lotto/domain/LottoFactory.java b/src/main/java/lotto/domain/LottoFactory.java new file mode 100644 index 0000000000..8315a0c24b --- /dev/null +++ b/src/main/java/lotto/domain/LottoFactory.java @@ -0,0 +1,30 @@ +package lotto.domain; + +import java.util.List; + +public class LottoFactory { + + private final LottoGenerator generator; + + public LottoFactory(LottoGenerator generator) { + validateGenerator(generator); + this.generator = generator; + } + + public List generate(int count) { + validateCount(count); + return generator.generate(count); + } + + private void validateGenerator(LottoGenerator generator) { + if (generator == null) { + throw new IllegalArgumentException("[ERROR] ๋กœ๋˜ ์ƒ์„ฑ๊ธฐ๋Š” null์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + } + + private void validateCount(int count) { + if (count <= 0) { + throw new IllegalArgumentException("[ERROR] ๋กœ๋˜ ์ƒ์„ฑ ๊ฐœ์ˆ˜๋Š” 1๊ฐœ ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + } +} diff --git a/src/main/java/lotto/domain/LottoGenerator.java b/src/main/java/lotto/domain/LottoGenerator.java new file mode 100644 index 0000000000..d3e9241185 --- /dev/null +++ b/src/main/java/lotto/domain/LottoGenerator.java @@ -0,0 +1,9 @@ +package lotto.domain; + +import java.util.List; + +public interface LottoGenerator { + + List generate(int count); + +} diff --git a/src/main/java/lotto/domain/LottoRank.java b/src/main/java/lotto/domain/LottoRank.java new file mode 100644 index 0000000000..d2496e1afe --- /dev/null +++ b/src/main/java/lotto/domain/LottoRank.java @@ -0,0 +1,37 @@ +package lotto.domain; + +import java.util.Arrays; + +public enum LottoRank { + + FIRST(6, false, 2_000_000_000L), + SECOND(5, true, 30_000_000L), + THIRD(5, false, 1_500_000L), + FOURTH(4, false, 50_000L), + FIFTH(3, false, 5_000L), + NONE(0, false, 0L); + + private final int matchCount; + private final boolean requiresBonus; + private final long prize; + + LottoRank(int matchCount, boolean requiresBonus, long prize) { + this.matchCount = matchCount; + this.requiresBonus = requiresBonus; + this.prize = prize; + } + + public long getPrize() { + return prize; + } + + public static LottoRank from(int matchCount, boolean bonusMatched) { + if (matchCount == 5) { + return bonusMatched ? SECOND : THIRD; + } + return Arrays.stream(values()) + .filter(rank -> rank.matchCount == matchCount) + .findFirst() + .orElse(NONE); + } +} diff --git a/src/main/java/lotto/domain/LottoResults.java b/src/main/java/lotto/domain/LottoResults.java new file mode 100644 index 0000000000..9a512b497c --- /dev/null +++ b/src/main/java/lotto/domain/LottoResults.java @@ -0,0 +1,71 @@ +package lotto.domain; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +public class LottoResults { + + private final Map results = new EnumMap<>(LottoRank.class); + private long totalPrize; + + public LottoResults(List lottos, WinningNumbers winningNumbers) { + validateLottos(lottos); + validateLottoElements(lottos); + validateWinningNumbers(winningNumbers); + countResults(lottos, winningNumbers); + } + + public int getCountOf(LottoRank rank) { + return results.getOrDefault(rank, 0); + } + + public long getTotalPrize() { + return totalPrize; + } + + public double calculateYield(long purchaseAmount) { + if (purchaseAmount <= 0) { + throw new IllegalArgumentException("[ERROR] ๊ตฌ์ž… ๊ธˆ์•ก์€ 0๋ณด๋‹ค ์ปค์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + return (double) totalPrize / purchaseAmount; + } + + private void validateLottos(List lottos) { + if (lottos == null) { + throw new IllegalArgumentException("[ERROR] ๋‹น์ฒจ ๊ฒฐ๊ณผ๋ฅผ ๊ณ„์‚ฐํ•  ๋กœ๋˜ ๋ชฉ๋ก์€ null์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + } + + private void validateLottoElements(List lottos) { + for (Lotto lotto : lottos) { + if (lotto == null) { + throw new IllegalArgumentException("[ERROR] ๋กœ๋˜ ๋ชฉ๋ก์— null์ด ํฌํ•จ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + } + } + + private void validateWinningNumbers(WinningNumbers winningNumbers) { + if (winningNumbers == null) { + throw new IllegalArgumentException("[ERROR] ๋‹น์ฒจ ๋ฒˆํ˜ธ๋Š” null์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + } + + private void countResults(List lottos, WinningNumbers winningNumbers) { + for (Lotto lotto : lottos) { + LottoRank rank = toRank(lotto, winningNumbers); + incrementRankCount(rank); + totalPrize += rank.getPrize(); + } + } + + private LottoRank toRank(Lotto lotto, WinningNumbers winningNumbers) { + int matchCount = winningNumbers.countMatchingNumbers(lotto); + boolean bonusMatched = winningNumbers.containsBonusNumber(lotto); + return LottoRank.from(matchCount, bonusMatched); + } + + private void incrementRankCount(LottoRank rank) { + results.put(rank, results.getOrDefault(rank, 0) + 1); + } +} diff --git a/src/main/java/lotto/domain/Money.java b/src/main/java/lotto/domain/Money.java new file mode 100644 index 0000000000..257fdfee61 --- /dev/null +++ b/src/main/java/lotto/domain/Money.java @@ -0,0 +1,33 @@ +package lotto.domain; + +public class Money { + + private static final int UNIT_MONEY_FOR_BUY_LOTTO = 1000; + private final int amount; + + public Money(int amount) { + validatePositive(amount); + validateUnit(amount); + this.amount = amount; + } + + public int getAmount() { + return amount; + } + + private void validatePositive(int amount) { + if (amount <= 0) { + throw new IllegalArgumentException("[ERROR] ๊ตฌ์ž… ๊ธˆ์•ก์€ 0๋ณด๋‹ค ์ปค์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + } + + private void validateUnit(int amount) { + if (amount % UNIT_MONEY_FOR_BUY_LOTTO != 0) { + throw new IllegalArgumentException("[ERROR] 1,000์› ๋‹จ์œ„๋กœ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + } + } + + public int getLottoCount() { + return amount / UNIT_MONEY_FOR_BUY_LOTTO; + } +} diff --git a/src/main/java/lotto/domain/RandomLottoGenerator.java b/src/main/java/lotto/domain/RandomLottoGenerator.java new file mode 100644 index 0000000000..4897ff372b --- /dev/null +++ b/src/main/java/lotto/domain/RandomLottoGenerator.java @@ -0,0 +1,23 @@ +package lotto.domain; + +import camp.nextstep.edu.missionutils.Randoms; + +import java.util.ArrayList; +import java.util.List; + +public class RandomLottoGenerator implements LottoGenerator { + + private static final int LOTTO_NUMBER_MIN = 1; + private static final int LOTTO_NUMBER_MAX = 45; + private static final int LOTTO_SIZE = 6; + + @Override + public List generate(int count) { + List lottos = new ArrayList<>(); + for (int i = 0; i < count; i++) { + List numbers = Randoms.pickUniqueNumbersInRange(LOTTO_NUMBER_MIN, LOTTO_NUMBER_MAX, LOTTO_SIZE); + lottos.add(new Lotto(numbers)); + } + return lottos; + } +} diff --git a/src/main/java/lotto/domain/WinningNumbers.java b/src/main/java/lotto/domain/WinningNumbers.java new file mode 100644 index 0000000000..b90dc0b219 --- /dev/null +++ b/src/main/java/lotto/domain/WinningNumbers.java @@ -0,0 +1,45 @@ +package lotto.domain; + +import java.util.List; + +public class WinningNumbers { + + private final Lotto winningLotto; + private final int bonusNumber; + + public WinningNumbers(List winningNumbers, int bonusNumber) { + Lotto lotto = new Lotto(winningNumbers); + validateBonusNumber(lotto, bonusNumber); + this.winningLotto = lotto; + this.bonusNumber = bonusNumber; + } + + public int getBonusNumber() { + return bonusNumber; + } + + public int countMatchingNumbers(Lotto lotto) { + return winningLotto.countMatchingNumbers(lotto); + } + + public boolean containsBonusNumber(Lotto lotto) { + return lotto.containsNumber(bonusNumber); + } + + private void validateBonusNumber(Lotto winningLotto, int bonusNumber) { + validateBonusRange(bonusNumber); + validateBonusDuplicate(winningLotto, bonusNumber); + } + + private void validateBonusRange(int bonusNumber) { + if (bonusNumber < 1 || bonusNumber > 45) { + throw new IllegalArgumentException("[ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” 1๋ถ€ํ„ฐ 45 ์‚ฌ์ด์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + } + + private void validateBonusDuplicate(Lotto winningLotto, int bonusNumber) { + if (winningLotto.containsNumber(bonusNumber)) { + throw new IllegalArgumentException("[ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ์ค‘๋ณต๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + } +} diff --git a/src/main/java/lotto/view/InputValidator.java b/src/main/java/lotto/view/InputValidator.java new file mode 100644 index 0000000000..17c772e86b --- /dev/null +++ b/src/main/java/lotto/view/InputValidator.java @@ -0,0 +1,54 @@ +package lotto.view; + +import java.util.Arrays; +import java.util.List; + +public class InputValidator { + + private static final String NUMBER_DELIMITER = ","; + + private InputValidator() { + } + + public static int parsePurchaseAmount(String input) { + validateNotBlank(input); + String trimmed = input.trim(); + try { + return Integer.parseInt(trimmed); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("[ERROR] ๊ตฌ์ž… ๊ธˆ์•ก์€ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + } + + public static List parseWinningNumbers(String input) { + validateNotBlank(input); + String[] parts = input.split(NUMBER_DELIMITER); + return Arrays.stream(parts) + .map(String::trim) + .map(part -> parseNumber(part, "[ERROR] ๋‹น์ฒจ ๋ฒˆํ˜ธ๋Š” ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.")) + .toList(); + } + + public static int parseBonusNumber(String input) { + validateNotBlank(input); + String trimmed = input.trim(); + return parseNumber(trimmed, "[ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + + private static void validateNotBlank(String input) { + if (input == null) { + throw new IllegalArgumentException("[ERROR] ์ž…๋ ฅ์€ ๋น„์–ด ์žˆ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + if (input.trim().isEmpty()) { + throw new IllegalArgumentException("[ERROR] ์ž…๋ ฅ์€ ๋น„์–ด ์žˆ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + } + + private static int parseNumber(String value, String errorMessage) { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(errorMessage); + } + } +} diff --git a/src/main/java/lotto/view/InputView.java b/src/main/java/lotto/view/InputView.java new file mode 100644 index 0000000000..6d48cbfb6f --- /dev/null +++ b/src/main/java/lotto/view/InputView.java @@ -0,0 +1,29 @@ +package lotto.view; + +import camp.nextstep.edu.missionutils.Console; + +import java.util.List; + +public class InputView { + + public static int readPurchaseAmount() { + System.out.println("๊ตฌ์ž…๊ธˆ์•ก์„ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + String input = Console.readLine(); + System.out.println(); + return InputValidator.parsePurchaseAmount(input); + } + + public static List readWinningNumbers() { + System.out.println("๋‹น์ฒจ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + String input = Console.readLine(); + System.out.println(); + return InputValidator.parseWinningNumbers(input); + } + + public static int readBonusNumber() { + System.out.println("๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + String input = Console.readLine(); + System.out.println(); + return InputValidator.parseBonusNumber(input); + } +} diff --git a/src/main/java/lotto/view/OutputView.java b/src/main/java/lotto/view/OutputView.java new file mode 100644 index 0000000000..60569371a9 --- /dev/null +++ b/src/main/java/lotto/view/OutputView.java @@ -0,0 +1,41 @@ +package lotto.view; + +import lotto.domain.Lotto; +import lotto.domain.LottoRank; +import lotto.domain.LottoResults; + +import java.util.List; + +public class OutputView { + + public static void printLottos(List lottos) { + System.out.printf("%d๊ฐœ๋ฅผ ๊ตฌ๋งคํ–ˆ์Šต๋‹ˆ๋‹ค.%n", lottos.size()); + for (Lotto lotto : lottos) { + System.out.println(lotto.getNumbers()); + } + } + + public static void printStatistics(LottoResults results) { + System.out.println("๋‹น์ฒจ ํ†ต๊ณ„"); + System.out.println("---"); + printRankResult(results, LottoRank.FIFTH, "3๊ฐœ ์ผ์น˜ (5,000์›) - %d๊ฐœ"); + printRankResult(results, LottoRank.FOURTH, "4๊ฐœ ์ผ์น˜ (50,000์›) - %d๊ฐœ"); + printRankResult(results, LottoRank.THIRD, "5๊ฐœ ์ผ์น˜ (1,500,000์›) - %d๊ฐœ"); + printRankResult(results, LottoRank.SECOND, "5๊ฐœ ์ผ์น˜, ๋ณด๋„ˆ์Šค ๋ณผ ์ผ์น˜ (30,000,000์›) - %d๊ฐœ"); + printRankResult(results, LottoRank.FIRST, "6๊ฐœ ์ผ์น˜ (2,000,000,000์›) - %d๊ฐœ"); + } + + private static void printRankResult(LottoResults results, LottoRank rank, String format) { + int count = results.getCountOf(rank); + System.out.printf(format + "%n", count); + } + + public static void printYield(double yield) { + double percentage = yield * 100; + System.out.printf("์ด ์ˆ˜์ต๋ฅ ์€ %.1f%%์ž…๋‹ˆ๋‹ค.%n", percentage); + } + + public static void printError(String message) { + System.out.println(message); + } +} diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java deleted file mode 100644 index 309f4e50ae..0000000000 --- a/src/test/java/lotto/LottoTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package lotto; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -class LottoTest { - @Test - void ๋กœ๋˜_๋ฒˆํ˜ธ์˜_๊ฐœ์ˆ˜๊ฐ€_6๊ฐœ๊ฐ€_๋„˜์–ด๊ฐ€๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { - assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 6, 7))) - .isInstanceOf(IllegalArgumentException.class); - } - - @DisplayName("๋กœ๋˜ ๋ฒˆํ˜ธ์— ์ค‘๋ณต๋œ ์ˆซ์ž๊ฐ€ ์žˆ์œผ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.") - @Test - void ๋กœ๋˜_๋ฒˆํ˜ธ์—_์ค‘๋ณต๋œ_์ˆซ์ž๊ฐ€_์žˆ์œผ๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { - assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 5))) - .isInstanceOf(IllegalArgumentException.class); - } - - // TODO: ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„์— ๋”ฐ๋ฅธ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ -} diff --git a/src/test/java/lotto/domain/FakeLottoGeneratorTest.java b/src/test/java/lotto/domain/FakeLottoGeneratorTest.java new file mode 100644 index 0000000000..13a66540ee --- /dev/null +++ b/src/test/java/lotto/domain/FakeLottoGeneratorTest.java @@ -0,0 +1,62 @@ +package lotto.domain; + +import lotto.domain.testDouble.FakeLottoGenerator; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.NoSuchElementException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class FakeLottoGeneratorTest { + + @Test + void ์ค€๋น„๋œ_๋ฒˆํ˜ธ_์‹œํ€€์Šค๋ฅผ_์‚ฌ์šฉํ•ด_์š”์ฒญํ•œ_๊ฐœ์ˆ˜๋งŒํผ_๋กœ๋˜๋ฅผ_์ƒ์„ฑํ•œ๋‹ค() { + + // given + List> numbersSequences = List.of( + List.of(1, 2, 3, 4, 5, 6), + List.of(7, 8, 9, 10, 11, 12) + ); + LottoGenerator generator = new FakeLottoGenerator(numbersSequences); + + // when + List lottos = generator.generate(2); + + // then + assertThat(lottos).hasSize(2); + assertThat(lottos.get(0).getNumbers()).containsExactly(1, 2, 3, 4, 5, 6); + assertThat(lottos.get(1).getNumbers()).containsExactly(7, 8, 9, 10, 11, 12); + } + + @Test + void ์ค€๋น„๋œ_๋ฒˆํ˜ธ๋ณด๋‹ค_๋งŽ์€_๊ฐœ์ˆ˜๋ฅผ_์š”์ฒญํ•˜๋ฉด_NoSuchElementException์ด_๋ฐœ์ƒํ•œ๋‹ค() { + + // given + List> numbersSequences = List.of( + List.of(1, 2, 3, 4, 5, 6) + ); + LottoGenerator generator = new FakeLottoGenerator(numbersSequences); + + // when & then + assertThatThrownBy(() -> generator.generate(2)) + .isInstanceOf(NoSuchElementException.class); + } + + @Test + void of_์ •์ _ํŒฉํ„ฐ๋ฆฌ_๋ฉ”์„œ๋“œ๋Š”_๋‹จ์ผ_๋ฒˆํ˜ธ_์„ธํŠธ๋กœ_๋กœ๋˜๋ฅผ_์ƒ์„ฑํ•œ๋‹ค() { + + // given + FakeLottoGenerator generator = FakeLottoGenerator.of( + List.of(1, 2, 3, 4, 5, 6) + ); + + // when + List lottos = generator.generate(1); + + // then + assertThat(lottos).hasSize(1); + assertThat(lottos.get(0).getNumbers()).containsExactly(1, 2, 3, 4, 5, 6); + } +} diff --git a/src/test/java/lotto/domain/LottoFactoryTest.java b/src/test/java/lotto/domain/LottoFactoryTest.java new file mode 100644 index 0000000000..a2f0bd5791 --- /dev/null +++ b/src/test/java/lotto/domain/LottoFactoryTest.java @@ -0,0 +1,61 @@ +package lotto.domain; + +import lotto.domain.testDouble.FakeLottoGenerator; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LottoFactoryTest { + + @Test + void ์œ ํšจํ•œ_์ƒ์„ฑ๊ธฐ์™€_๊ฐœ์ˆ˜๋กœ_๋กœ๋˜_๋ชฉ๋ก์„_์ƒ์„ฑํ•œ๋‹ค() { + + // given + FakeLottoGenerator generator = new FakeLottoGenerator( + List.of( + List.of(1, 2, 3, 4, 5, 6), + List.of(7, 8, 9, 10, 11, 12), + List.of(13, 14, 15, 16, 17, 18) + ) + ); + LottoFactory factory = new LottoFactory(generator); + int count = 3; + + // when + List lottos = factory.generate(count); + + // then + assertThat(lottos).hasSize(count); + assertThat(lottos).allSatisfy(lotto -> assertThat(lotto).isNotNull()); + } + + @ParameterizedTest + @NullSource + void ์ƒ์„ฑ๊ธฐ๊ฐ€_null์ด๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค(LottoGenerator generator) { + + // when & then + assertThatThrownBy(() -> new LottoFactory(generator)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋กœ๋˜ ์ƒ์„ฑ๊ธฐ๋Š” null์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + + @ParameterizedTest + @ValueSource(ints = {0, -1, -10}) + void ์ƒ์„ฑ_๊ฐœ์ˆ˜๊ฐ€_0_์ดํ•˜์ด๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค(int count) { + + // given + LottoGenerator generator = FakeLottoGenerator.of(List.of(1, 2, 3, 4, 5, 6)); + LottoFactory factory = new LottoFactory(generator); + + // when & then + assertThatThrownBy(() -> factory.generate(count)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋กœ๋˜ ์ƒ์„ฑ ๊ฐœ์ˆ˜๋Š” 1๊ฐœ ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } +} diff --git a/src/test/java/lotto/domain/LottoRankTest.java b/src/test/java/lotto/domain/LottoRankTest.java new file mode 100644 index 0000000000..ffbb85c6a7 --- /dev/null +++ b/src/test/java/lotto/domain/LottoRankTest.java @@ -0,0 +1,152 @@ +package lotto.domain; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class LottoRankTest { + + @Test + void ์—ฌ์„ฏ_๊ฐœ๊ฐ€_๋ชจ๋‘_์ผ์น˜ํ•˜๋ฉด_1๋“ฑ์ด๋‹ค() { + + // given + int matchCount = 6; + boolean bonusMatched = false; + + // when + LottoRank rank = LottoRank.from(matchCount, bonusMatched); + + // then + assertThat(rank).isEqualTo(LottoRank.FIRST); + } + + @Test + void ๋‹ค์„ฏ_๊ฐœ์™€_๋ณด๋„ˆ์Šค๊นŒ์ง€_์ผ์น˜ํ•˜๋ฉด_2๋“ฑ์ด๋‹ค() { + + // given + int matchCount = 5; + boolean bonusMatched = true; + + // when + LottoRank rank = LottoRank.from(matchCount, bonusMatched); + + // then + assertThat(rank).isEqualTo(LottoRank.SECOND); + } + + @Test + void ๋‹ค์„ฏ_๊ฐœ๋งŒ_์ผ์น˜ํ•˜๊ณ _๋ณด๋„ˆ์Šค๋Š”_๋‹ค๋ฅด๋ฉด_3๋“ฑ์ด๋‹ค() { + + // given + int matchCount = 5; + boolean bonusMatched = false; + + // when + LottoRank rank = LottoRank.from(matchCount, bonusMatched); + + // then + assertThat(rank).isEqualTo(LottoRank.THIRD); + } + + @Test + void ๋„ค_๊ฐœ๊ฐ€_์ผ์น˜ํ•˜๋ฉด_4๋“ฑ์ด๋‹ค() { + + // given + int matchCount = 4; + + // when + LottoRank rank = LottoRank.from(matchCount, false); + + // then + assertThat(rank).isEqualTo(LottoRank.FOURTH); + } + + @Test + void ์„ธ_๊ฐœ๊ฐ€_์ผ์น˜ํ•˜๋ฉด_5๋“ฑ์ด๋‹ค() { + + // given + int matchCount = 3; + + // when + LottoRank rank = LottoRank.from(matchCount, false); + + // then + assertThat(rank).isEqualTo(LottoRank.FIFTH); + } + + @ParameterizedTest + @ValueSource(ints = {0, 1, 2}) + void ๋‘_๊ฐœ_์ดํ•˜๋กœ_์ผ์น˜ํ•˜๋ฉด_๊ฝ์ด๋‹ค(int matchCount) { + + // when + LottoRank rank = LottoRank.from(matchCount, false); + + // then + assertThat(rank).isEqualTo(LottoRank.NONE); + } + + @ParameterizedTest + @MethodSource("providePrizePerRank") + void ๋“ฑ์ˆ˜๋ณ„_๋‹น์ฒจ๊ธˆ์„_๋ฐ˜ํ™˜ํ•œ๋‹ค(LottoRank rank, long expectedPrize) { + + //when + long prize = rank.getPrize(); + + // then + assertThat(prize).isEqualTo(expectedPrize); + } + + + @ParameterizedTest + @MethodSource("bonusShouldBeIgnoredCases") + void ๋ณด๋„ˆ์Šค๋Š”_๋‹ค์„ฏ_๊ฐœ๊ฐ€_๋งž์„_๋•Œ๋ฅผ_์ œ์™ธํ•˜๊ณ ๋Š”_์˜ํ–ฅ์„_์ฃผ์ง€_์•Š๋Š”๋‹ค(int matchCount) { + + // when + LottoRank withBonus = LottoRank.from(matchCount, true); + LottoRank withoutBonus = LottoRank.from(matchCount, false); + + // then + assertThat(withBonus).isEqualTo(withoutBonus); + } + + @Test + void ๋‹ค์„ฏ_๊ฐœ_์ผ์น˜_์‹œ_๋ณด๋„ˆ์Šค_์—ฌ๋ถ€์—_๋”ฐ๋ผ_๋“ฑ์ˆ˜๊ฐ€_๋ฐ”๋€๋‹ค() { + + // when + LottoRank withBonus = LottoRank.from(5, true); + LottoRank withoutBonus = LottoRank.from(5, false); + + // then + assertThat(withBonus).isEqualTo(LottoRank.SECOND); + assertThat(withoutBonus).isEqualTo(LottoRank.THIRD); + assertThat(withBonus).isNotEqualTo(withoutBonus); + } + + private static Stream bonusShouldBeIgnoredCases() { + return Stream.of( + Arguments.of(0), + Arguments.of(1), + Arguments.of(2), + Arguments.of(3), + Arguments.of(4), + Arguments.of(6) + ); + } + + private static Stream providePrizePerRank() { + return Stream.of( + Arguments.of(LottoRank.FIRST, 2_000_000_000L), + Arguments.of(LottoRank.SECOND, 30_000_000L), + Arguments.of(LottoRank.THIRD, 1_500_000L), + Arguments.of(LottoRank.FOURTH, 50_000L), + Arguments.of(LottoRank.FIFTH, 5_000L), + Arguments.of(LottoRank.NONE, 0L) + ); + } +} diff --git a/src/test/java/lotto/domain/LottoResultsTest.java b/src/test/java/lotto/domain/LottoResultsTest.java new file mode 100644 index 0000000000..fbd85777bf --- /dev/null +++ b/src/test/java/lotto/domain/LottoResultsTest.java @@ -0,0 +1,157 @@ +package lotto.domain; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LottoResultsTest { + + @Test + void ์ฃผ์–ด์ง„_๋กœ๋˜๋“ค๊ณผ_๋‹น์ฒจ_๋ฒˆํ˜ธ_๋กœ๋˜๋ฅผ_๋น„๊ตํ•˜์—ฌ_์ˆœ์œ„๋ฅผ_์˜ฌ๋ฆฌ๊ณ _์ง‘๊ณ„ํ• _์ˆ˜_์žˆ๋‹ค() { + + // given + WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7); + Lotto first = new Lotto(List.of(1, 2, 3, 4, 5, 6)); + Lotto second = new Lotto(List.of(1, 2, 3, 4, 5, 7)); + Lotto third = new Lotto(List.of(1, 2, 3, 4, 5, 45)); + Lotto fourth = new Lotto(List.of(1, 2, 3, 4, 11, 45)); + Lotto fifth = new Lotto(List.of(1, 2, 3, 7, 11, 45)); + Lotto none = new Lotto(List.of(10, 11, 12, 13, 14, 15)); + List lottos = List.of(first, second, third, fourth, fifth, none); + + // when + LottoResults results = new LottoResults(lottos, winningNumbers); + + // then + assertThat(results.getCountOf(LottoRank.FIRST)).isEqualTo(1); + assertThat(results.getCountOf(LottoRank.SECOND)).isEqualTo(1); + assertThat(results.getCountOf(LottoRank.THIRD)).isEqualTo(1); + assertThat(results.getCountOf(LottoRank.FOURTH)).isEqualTo(1); + assertThat(results.getCountOf(LottoRank.FIFTH)).isEqualTo(1); + assertThat(results.getCountOf(LottoRank.NONE)).isEqualTo(1); + } + + @Test + void ์ฃผ์–ด์ง„_๋กœ๋˜๋“ค๊ณผ_๋‹น์ฒจ_๋ฒˆํ˜ธ_๋กœ๋˜์˜_์ผ์น˜_๊ฐœ์ˆ˜๊ฐ€_2_์ดํ•˜๋ผ๋ฉด_NONE๋งŒ_์ฆ๊ฐ€ํ•œ๋‹ค() { + + // given + WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7); + List lottos = List.of( + new Lotto(List.of(10, 11, 12, 13, 14, 15)), + new Lotto(List.of(20, 21, 22, 23, 24, 25)), + new Lotto(List.of(1, 2, 22, 23, 24, 25)), + new Lotto(List.of(20, 21, 3, 23, 24, 25)), + new Lotto(List.of(20, 21, 3, 4, 24, 25)), + new Lotto(List.of(20, 21, 10, 11, 5, 25)), + new Lotto(List.of(20, 21, 10, 11, 5, 6)) + ); + + // when + LottoResults results = new LottoResults(lottos, winningNumbers); + + // then + for (LottoRank rank : LottoRank.values()) { + if (rank == LottoRank.NONE) { + assertThat(results.getCountOf(rank)).isEqualTo(7); + continue; + } + assertThat(results.getCountOf(rank)).isEqualTo(0); + } + } + + @Test + void ๋‹น์ฒจ๋œ_๋กœ๋˜๋“ค์˜_์ด_๋‹น์ฒจ๊ธˆ์„_ํ•ฉ์‚ฐํ•œ๋‹ค() { + + // given + WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7); + Lotto first = new Lotto(List.of(1, 2, 3, 4, 5, 6)); + Lotto fifth = new Lotto(List.of(1, 2, 3, 40, 41, 42)); + List lottos = List.of(first, fifth); + + // when + LottoResults results = new LottoResults(lottos, winningNumbers); + + // then + assertThat(results.getTotalPrize()).isEqualTo(2_000_005_000L); + } + + @Test + void ์ˆ˜์ต๋ฅ ์„_๊ตฌ์ž…๊ธˆ์•ก์œผ๋กœ_๊ณ„์‚ฐํ•œ๋‹ค() { + + // given + WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7); + Lotto first = new Lotto(List.of(1, 2, 3, 4, 5, 6)); + Lotto third = new Lotto(List.of(1, 2, 3, 4, 17, 6)); + List lottos = List.of(first, third); + LottoResults results = new LottoResults(lottos, winningNumbers); + + // when + double yield = results.calculateYield(lottos.size() * 1000); + + // then + assertThat(yield).isEqualTo(2_001_500_000D / 2_000D); + } + + @ParameterizedTest + @NullSource + void ๋กœ๋˜_๋ชฉ๋ก์ด_null์ด๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค(List lottos) { + + // given + WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7); + + // when & then + assertThatThrownBy(() -> new LottoResults(lottos, winningNumbers)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋‹น์ฒจ ๊ฒฐ๊ณผ๋ฅผ ๊ณ„์‚ฐํ•  ๋กœ๋˜ ๋ชฉ๋ก์€ null์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + + @Test + void ๋กœ๋˜_๋ชฉ๋ก์—_null์ด_ํฌํ•จ๋˜๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + + // given + WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7); + List lottos = new ArrayList<>(); + lottos.add(new Lotto(List.of(1, 2, 3, 4, 5, 6))); + lottos.add(null); + + // when & then + assertThatThrownBy(() -> new LottoResults(lottos, winningNumbers)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋กœ๋˜ ๋ชฉ๋ก์— null์ด ํฌํ•จ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + + @Test + void ๋‹น์ฒจ๋ฒˆํ˜ธ๊ฐ€_null์ด๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + // given + List lottos = List.of(new Lotto(List.of(1, 2, 3, 4, 5, 6))); + + // when & then + assertThatThrownBy(() -> new LottoResults(lottos, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋‹น์ฒจ ๋ฒˆํ˜ธ๋Š” null์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + + @ParameterizedTest + @ValueSource(ints = {0, -10}) + void ์ˆ˜์ต๋ฅ _๊ณ„์‚ฐ์‹œ_๊ตฌ์ž…๊ธˆ์•ก์ด_0_์ดํ•˜์ด๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค(int purchaseAmount) { + + // given + WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7); + LottoResults results = new LottoResults( + List.of(new Lotto(List.of(1, 2, 3, 4, 5, 6))), + winningNumbers + ); + + // when & then + assertThatThrownBy(() -> results.calculateYield(purchaseAmount)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๊ตฌ์ž… ๊ธˆ์•ก์€ 0๋ณด๋‹ค ์ปค์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } +} diff --git a/src/test/java/lotto/domain/LottoTest.java b/src/test/java/lotto/domain/LottoTest.java new file mode 100644 index 0000000000..d535420017 --- /dev/null +++ b/src/test/java/lotto/domain/LottoTest.java @@ -0,0 +1,177 @@ +package lotto.domain; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.*; + +class LottoTest { + + @Test + void ๋กœ๋˜_๋ฒˆํ˜ธ๊ฐ€_6๊ฐœ๋ณด๋‹ค_๋งŽ์œผ๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + + // given + List numbers = List.of(1, 2, 3, 4, 5, 6, 7); + + // when & then + assertThatThrownBy(() -> new Lotto(numbers)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 6๊ฐœ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + + @Test + void ๋กœ๋˜_๋ฒˆํ˜ธ๊ฐ€_6๊ฐœ๋ณด๋‹ค_์ ์œผ๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + + // given + List numbers = List.of(1, 2, 3, 4, 5); + + // when & then + assertThatThrownBy(() -> new Lotto(numbers)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 6๊ฐœ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + + @Test + void ๋กœ๋˜_๋ฒˆํ˜ธ์—_์ค‘๋ณต๋œ_์ˆซ์ž๊ฐ€_์žˆ์œผ๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + + // given + List numbers = List.of(1, 2, 3, 4, 5, 5); + + // when & then + assertThatThrownBy(() -> new Lotto(numbers)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” ์ค‘๋ณต๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + + @ParameterizedTest() + @ValueSource(ints = {0, 46}) + void ๋กœ๋˜_๋ฒˆํ˜ธ๊ฐ€_1๋ถ€ํ„ฐ_45_์‚ฌ์ด์˜_๊ฐ’์ด_์•„๋‹ˆ๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค(int invalidNumber) { + + // given + List numbers = List.of(1, 2, 3, 4, 5, invalidNumber); + + // when & then + assertThatThrownBy(() -> new Lotto(numbers)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” 1๋ถ€ํ„ฐ 45 ์‚ฌ์ด์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + + @ParameterizedTest + @NullSource + void ๋กœ๋˜_๋ฒˆํ˜ธ๊ฐ€_null์ด๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค(List numbers) { + + // when & then + assertThatThrownBy(() -> new Lotto(numbers)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” null์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + + @Test + void ๋กœ๋˜_๋ฒˆํ˜ธ๊ฐ€_์ •์ƒ_๋ฒ”์œ„์—์„œ_๊ทน_๊ฐ’์ธ_1๊ณผ_45๋ฅผ_ํฌํ•จํ•ด๋„_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•˜์ง€_์•Š๋Š”๋‹ค() { + + // given + List numbers = List.of(1, 2, 3, 4, 5, 45); + + // when & then + assertThatNoException().isThrownBy(() -> new Lotto(numbers)); + } + + @Test + void ์ •์ƒ_๋ฒ”์œ„์˜_6๊ฐœ_๊ฐ’์„_์‚ฌ์šฉํ•˜๋ฉด_๊ฐ์ฒด_์ƒ์„ฑ์ด_๊ฐ€๋Šฅํ•˜๋‹ค() { + + // given + List numbers = List.of(8, 1, 45, 3, 20, 10); + + // when + Lotto lotto = new Lotto(numbers); + + // then + assertThat(lotto).isNotNull(); + assertThat(lotto.getNumbers()).hasSize(6); + } + + @Test + void ๋กœ๋˜_๋ฒˆํ˜ธ๋Š”_์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ_์ •๋ ฌ๋˜์–ด_์ €์žฅ๋œ๋‹ค() { + + // given + List numbers = List.of(8, 1, 45, 3, 20, 10); + + // when + Lotto lotto = new Lotto(numbers); + + // then + assertThat(lotto.getNumbers()).containsExactly(1, 3, 8, 10, 20, 45); + } + + @Test + void ๋กœ๋˜๊ฐ€_ํŠน์ •_๋ฒˆํ˜ธ๋ฅผ_ํฌํ•จํ•˜๋ฉด_true๋ฅผ_๋ฐ˜ํ™˜ํ•œ๋‹ค() { + + // given + Lotto lotto = new Lotto(List.of(1, 3, 5, 10, 20, 45)); + + // when & then + assertThat(lotto.containsNumber(10)).isTrue(); + assertThat(lotto.containsNumber(7)).isFalse(); + } + + @ParameterizedTest + @MethodSource("provideLottoMatchScenarios") + void ๋‹ค๋ฅธ_๋กœ๋˜์™€_์ผ์น˜ํ•˜๋Š”_๋ฒˆํ˜ธ_๊ฐœ์ˆ˜๋ฅผ_๊ณ„์‚ฐํ• _์ˆ˜_์žˆ๋‹ค(Lotto priorLotto, Lotto nextLotto, int expectedMatchCount) { + + // when + int matchCount = priorLotto.countMatchingNumbers(nextLotto); + + // then + assertThat(matchCount).isEqualTo(expectedMatchCount); + } + + @Test + void ๋น„๊ตํ• _๋กœ๋˜๊ฐ€_null์ด๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + + // given + Lotto lotto = new Lotto(List.of(1, 2, 3, 4, 5, 6)); + + // when & then + assertThatThrownBy(() -> lotto.countMatchingNumbers(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋น„๊ตํ•  ๋กœ๋˜๊ฐ€ null์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + + @Test + void ๋กœ๋˜_๋ฒˆํ˜ธ_๋ชฉ๋ก์€_์™ธ๋ถ€์—์„œ_์ˆ˜์ •ํ• _์ˆ˜_์—†๋‹ค() { + + // given + Lotto lotto = new Lotto(List.of(1, 2, 3, 4, 5, 6)); + + // when & then + assertThatThrownBy(() -> lotto.getNumbers().add(7)) + .isInstanceOf(UnsupportedOperationException.class); + } + + private static Stream provideLottoMatchScenarios() { + return Stream.of( + Arguments.of( + new Lotto(List.of(1, 2, 3, 4, 5, 6)), + new Lotto(List.of(4, 5, 6, 40, 41, 42)), + 3 + ), + Arguments.of( + new Lotto(List.of(1, 2, 3, 4, 5, 6)), + new Lotto(List.of(7, 8, 9, 10, 11, 12)), + 0 + ), + Arguments.of( + new Lotto(List.of(1, 2, 3, 4, 5, 6)), + new Lotto(List.of(1, 2, 3, 4, 5, 6)), + 6 + ) + ); + } +} diff --git a/src/test/java/lotto/domain/MoneyTest.java b/src/test/java/lotto/domain/MoneyTest.java new file mode 100644 index 0000000000..f28728b1dc --- /dev/null +++ b/src/test/java/lotto/domain/MoneyTest.java @@ -0,0 +1,53 @@ +package lotto.domain; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class MoneyTest { + + private static final int UNIT_MONEY_FOR_BUY_LOTTO = 1000; + + @ParameterizedTest + @ValueSource(ints = {1000, 5000, 1000000000}) + void ๋กœ๋˜_๊ตฌ๋งค_๊ธˆ์•ก์ด_์ฒœ_์›์œผ๋กœ_๋‚˜๋ˆ ์ง€๋ฉด_Money_๊ฐ์ฒด๊ฐ€_์ƒ์„ฑ๋œ๋‹ค(int amount) { + // when + Money money = new Money(amount); + + // then + assertThat(money).isNotNull(); + } + + @ParameterizedTest + @ValueSource(ints = {500, 1100, 2500}) + void ๋กœ๋˜_๊ตฌ๋งค_๊ธˆ์•ก์ด_์ฒœ_์›์œผ๋กœ_๋‚˜๋ˆ ์ง€์ง€_์•Š์œผ๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค(int amount) { + // when & then + assertThatThrownBy(() -> new Money(amount)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] 1,000์› ๋‹จ์œ„๋กœ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”."); + } + + @ParameterizedTest + @ValueSource(ints = {0, -1000, -5000}) + void ๋กœ๋˜_๊ตฌ๋งค_๊ธˆ์•ก์ด_0์›_์ดํ•˜๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค(int amount) { + // when & then + assertThatThrownBy(() -> new Money(amount)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๊ตฌ์ž… ๊ธˆ์•ก์€ 0๋ณด๋‹ค ์ปค์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + + @ParameterizedTest + @ValueSource(ints = {1000, 3000, 100000}) + void ๋กœ๋˜_๊ตฌ๋งค_๊ธˆ์•ก์„_์ž…๋ ฅํ•˜๋ฉด_๊ตฌ๋งค_๊ฐ€๋Šฅํ•œ_๋กœ๋˜_๊ฐœ์ˆ˜๋ฅผ_๋ฐ˜ํ™˜ํ•œ๋‹ค(int amount) { + // given + Money money = new Money(amount); + + // when + int count = money.getLottoCount(); + + // then + assertThat(count).isEqualTo(amount / UNIT_MONEY_FOR_BUY_LOTTO); + } +} diff --git a/src/test/java/lotto/domain/RandomLottoGeneratorTest.java b/src/test/java/lotto/domain/RandomLottoGeneratorTest.java new file mode 100644 index 0000000000..18d47aa941 --- /dev/null +++ b/src/test/java/lotto/domain/RandomLottoGeneratorTest.java @@ -0,0 +1,25 @@ +package lotto.domain; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class RandomLottoGeneratorTest { + + @Test + void ๋กœ๋˜_์ƒ์„ฑ_์š”์ฒญ_๊ฐœ์ˆ˜๋งŒํผ_๋žœ๋ค_๋กœ๋˜๋ฅผ_์ƒ์„ฑํ•œ๋‹ค() { + + // given + LottoGenerator generator = new RandomLottoGenerator(); + int count = 5; + + // when + List lottos = generator.generate(count); + + // then + assertThat(lottos).hasSize(count) + .doesNotContainNull(); + } +} diff --git a/src/test/java/lotto/domain/WinningNumbersTest.java b/src/test/java/lotto/domain/WinningNumbersTest.java new file mode 100644 index 0000000000..f802764dc6 --- /dev/null +++ b/src/test/java/lotto/domain/WinningNumbersTest.java @@ -0,0 +1,106 @@ +package lotto.domain; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class WinningNumbersTest { + + @ParameterizedTest + @ValueSource(ints = {1, 45}) + void ๋ณด๋„ˆ์Šค_๋ฒˆํ˜ธ๊ฐ€_์ •์ƒ_๋ฒ”์œ„์—์„œ_๊ทน_๊ฐ’์ธ_1๊ณผ_45_๊ฐ’์ด_๋“ค์–ด๊ฐ€๋„_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•˜์ง€_์•Š๋Š”๋‹ค(int validBonusNumber) { + + // given + List numbers = List.of(2, 3, 4, 5, 6, 7); + + // when & then + assertThatNoException() + .isThrownBy(() -> new WinningNumbers(numbers, validBonusNumber)); + } + + @ParameterizedTest + @ValueSource(ints = {0, 46}) + void ๋ณด๋„ˆ์Šค_๋ฒˆํ˜ธ๊ฐ€_์ •์ƒ_๋ฒ”์œ„๋ฅผ_๋ฒ—์–ด๋‚œ_1๋ถ€ํ„ฐ_45_์‚ฌ์ด์˜_๊ฐ’์ด_์•„๋‹ˆ๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค(int invalidBonusNumber) { + + // given + List numbers = List.of(1, 2, 3, 4, 5, 6); + + // when & then + assertThatThrownBy(() -> new WinningNumbers(numbers, invalidBonusNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” 1๋ถ€ํ„ฐ 45 ์‚ฌ์ด์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + + @Test + void ๋ณด๋„ˆ์Šค_๋ฒˆํ˜ธ๊ฐ€_๋‹น์ฒจ_๋ฒˆํ˜ธ์™€_์ค‘๋ณต๋˜๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + + // given + List numbers = List.of(1, 2, 3, 4, 5, 6); + + // when & then + assertThatThrownBy(() -> new WinningNumbers(numbers, 6)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” ๋‹น์ฒจ ๋ฒˆํ˜ธ์™€ ์ค‘๋ณต๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + + @Test + void ๋‹น์ฒจ_๋ฒˆํ˜ธ_๋กœ๋˜์˜_์ƒ์„ฑ์ž_๊ฒ€์ฆ์ด_๋ณด๋„ˆ์Šค_๋ฒˆํ˜ธ_๊ฒ€์ฆ๋ณด๋‹ค_๋จผ์ €_์ˆ˜ํ–‰๋œ๋‹ค() { + + // given + List invalidNumbers = List.of(1, 2, 3, 4, 5, 5); + + // when & then + assertThatThrownBy(() -> new WinningNumbers(invalidNumbers, 7)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋กœ๋˜ ๋ฒˆํ˜ธ๋Š” ์ค‘๋ณต๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + + @Test + void ๋‹น์ฒจ_๋ฒˆํ˜ธ์™€_๋ณด๋„ˆ์Šค_๋ฒˆํ˜ธ๊ฐ€_๋ชจ๋‘_์œ ํšจํ•˜๋ฉด_์ƒ์„ฑ๋œ๋‹ค() { + + // given + List lottoNumbers = List.of(4, 36, 3, 28, 44, 11); + int bonusNumber = 7; + + // when + WinningNumbers winningNumbers = new WinningNumbers(lottoNumbers, bonusNumber); + + // then + assertThat(winningNumbers).isNotNull(); + assertThat(winningNumbers.getBonusNumber()).isEqualTo(bonusNumber); + + Lotto testLotto = new Lotto(lottoNumbers); + assertThat(winningNumbers.countMatchingNumbers(testLotto)).isEqualTo(6); + } + + @Test + void ๋‹น์ฒจ_๋ฒˆํ˜ธ_๋กœ๋˜์™€_๋น„๊ตํ• _๋กœ๋˜์˜_๊ฐ’_์ผ์น˜_๊ฐœ์ˆ˜๋ฅผ_๊ตฌํ• _์ˆ˜_์žˆ๋‹ค() { + + // given + WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7); + Lotto lotto = new Lotto(List.of(1, 2, 10, 11, 12, 13)); + + // when + int matchCount = winningNumbers.countMatchingNumbers(lotto); + + // then + assertThat(matchCount).isEqualTo(2); + } + + @Test + void ๋ณด๋„ˆ์Šค_๋ฒˆํ˜ธ๋ฅผ_ํฌํ•จํ•˜๋Š”์ง€_ํ™•์ธํ• _์ˆ˜_์žˆ๋‹ค() { + + // given + WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7); + Lotto containsBonus = new Lotto(List.of(7, 10, 20, 30, 40, 45)); + Lotto notContainsBonus = new Lotto(List.of(8, 10, 20, 30, 40, 45)); + + // when & then + assertThat(winningNumbers.containsBonusNumber(containsBonus)).isTrue(); + assertThat(winningNumbers.containsBonusNumber(notContainsBonus)).isFalse(); + } +} diff --git a/src/test/java/lotto/domain/testDouble/FakeLottoGenerator.java b/src/test/java/lotto/domain/testDouble/FakeLottoGenerator.java new file mode 100644 index 0000000000..012f2aed3c --- /dev/null +++ b/src/test/java/lotto/domain/testDouble/FakeLottoGenerator.java @@ -0,0 +1,31 @@ +package lotto.domain.testDouble; + +import lotto.domain.Lotto; +import lotto.domain.LottoGenerator; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class FakeLottoGenerator implements LottoGenerator { + + private final Iterator> numbersIterator; + + public FakeLottoGenerator(List> numbersSequences) { + this.numbersIterator = numbersSequences.iterator(); + } + + @Override + public List generate(int count) { + List lottos = new ArrayList<>(); + for (int i = 0; i < count; i++) { + List numbers = numbersIterator.next(); + lottos.add(new Lotto(numbers)); + } + return lottos; + } + + public static FakeLottoGenerator of(List numbers) { + return new FakeLottoGenerator(List.of(numbers)); + } +} diff --git a/src/test/java/lotto/view/InputValidatorTest.java b/src/test/java/lotto/view/InputValidatorTest.java new file mode 100644 index 0000000000..e6da7d9c90 --- /dev/null +++ b/src/test/java/lotto/view/InputValidatorTest.java @@ -0,0 +1,132 @@ +package lotto.view; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class InputValidatorTest { + + @Test + void ๊ตฌ์ž…_๊ธˆ์•ก_๋ฌธ์ž์—ด์„_์ •์ˆ˜๋กœ_ํŒŒ์‹ฑํ•œ๋‹ค() { + + // given + String input = "3000"; + + // when + int amount = InputValidator.parsePurchaseAmount(input); + + // then + assertThat(amount).isEqualTo(3000); + } + + @ParameterizedTest + @NullSource + @ValueSource(strings = {"", " ", " "}) + void ๊ตฌ์ž…_๊ธˆ์•ก_์ž…๋ ฅ์ด_null_๋˜๋Š”_๊ณต๋ฐฑ์ด๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค(String input) { + + // when & then + assertThatThrownBy(() -> InputValidator.parsePurchaseAmount(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์ž…๋ ฅ์€ ๋น„์–ด ์žˆ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + + @ParameterizedTest + @ValueSource(strings = {"abc", "1000์›", "1 000", "1,000"}) + void ๊ตฌ์ž…_๊ธˆ์•ก์—_์ˆซ์ž๊ฐ€_์•„๋‹Œ_๊ฐ’์ด_ํฌํ•จ๋˜๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค(String input) { + + // when & then + assertThatThrownBy(() -> InputValidator.parsePurchaseAmount(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๊ตฌ์ž… ๊ธˆ์•ก์€ ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + + @Test + void ์‰ผํ‘œ๋กœ_๊ตฌ๋ถ„๋œ_๋‹น์ฒจ_๋ฒˆํ˜ธ๋ฅผ_์ •์ˆ˜_๋ฆฌ์ŠคํŠธ๋กœ_ํŒŒ์‹ฑํ•œ๋‹ค() { + + // given + String input = "1,2,3,4,5,6"; + + // when + List numbers = InputValidator.parseWinningNumbers(input); + + // then + assertThat(numbers).containsExactly(1, 2, 3, 4, 5, 6); + } + + @Test + void ๋‹น์ฒจ_๋ฒˆํ˜ธ_ํŒŒ์‹ฑ์‹œ_๊ณต๋ฐฑ์„_๋ฌด์‹œํ•œ๋‹ค() { + + // given + String input = " 1, 2 ,3 , 4,5 ,6 "; + + // when + List numbers = InputValidator.parseWinningNumbers(input); + + // then + assertThat(numbers).containsExactly(1, 2, 3, 4, 5, 6); + } + + @ParameterizedTest + @NullSource + @ValueSource(strings = {"", " ", " "}) + void ๋‹น์ฒจ_๋ฒˆํ˜ธ_์ž…๋ ฅ์ด_null_๋˜๋Š”_๊ณต๋ฐฑ์ด๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค(String input) { + + // when & then + assertThatThrownBy(() -> InputValidator.parseWinningNumbers(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์ž…๋ ฅ์€ ๋น„์–ด ์žˆ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + + @Test + void ๋‹น์ฒจ_๋ฒˆํ˜ธ์—_์ˆซ์ž๊ฐ€_์•„๋‹Œ_๊ฐ’์ด_ํฌํ•จ๋˜๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + + // given + String input = "1, 2, ์„ธ, 4, 5, 6"; + + // when & then + assertThatThrownBy(() -> InputValidator.parseWinningNumbers(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋‹น์ฒจ ๋ฒˆํ˜ธ๋Š” ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } + + @Test + void ๋ณด๋„ˆ์Šค_๋ฒˆํ˜ธ_๋ฌธ์ž์—ด์„_์ •์ˆ˜๋กœ_ํŒŒ์‹ฑํ•œ๋‹ค() { + + // given + String input = "7"; + + // when + int bonus = InputValidator.parseBonusNumber(input); + + // then + assertThat(bonus).isEqualTo(7); + } + + @ParameterizedTest + @NullSource + @ValueSource(strings = {"", " ", " "}) + void ๋ณด๋„ˆ์Šค_๋ฒˆํ˜ธ_์ž…๋ ฅ์ด_null_๋˜๋Š”_๊ณต๋ฐฑ์ด๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค(String input) { + + // when & then + assertThatThrownBy(() -> InputValidator.parseBonusNumber(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ์ž…๋ ฅ์€ ๋น„์–ด ์žˆ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + + @Test + void ๋ณด๋„ˆ์Šค_๋ฒˆํ˜ธ์—_์ˆซ์ž๊ฐ€_์•„๋‹Œ_๊ฐ’์ด_๋“ค์–ด์˜ค๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() { + // given + String input = "๋ณด๋„ˆ์Šค"; + + // when & then + assertThatThrownBy(() -> InputValidator.parseBonusNumber(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("[ERROR] ๋ณด๋„ˆ์Šค ๋ฒˆํ˜ธ๋Š” ์ˆซ์ž์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค."); + } +}