Skip to content

Commit 9373278

Browse files
committed
Finish the book
1 parent f8316e8 commit 9373278

File tree

16 files changed

+1584
-335
lines changed

16 files changed

+1584
-335
lines changed

slides/ch14.md

Lines changed: 547 additions & 0 deletions
Large diffs are not rendered by default.

slides/ch14.pdf

431 KB
Binary file not shown.

slides/ch15.md

Lines changed: 302 additions & 6 deletions
Large diffs are not rendered by default.

slides/ch15.pdf

267 KB
Binary file not shown.

slides/ch6.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ let nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
6565

6666
## 벡터
6767

68-
벡터는 러스트에서 가장 널리 사용되는 자료형 중 하나로, 여러 개의 값을 하나로 묶어서 사용할 수 있습니다. 벡터의 특징은 길이를 런타임에 동적으로 변경 가능하다는 점입니다. 이러한 특징 떄문에 런타임에서는 값이 힙 영역에 저장됩니다.
68+
벡터는 러스트에서 가장 널리 사용되는 자료형 중 하나로, 여러 개의 값을 하나로 묶어서 사용할 수 있습니다. 벡터의 특징은 길이를 런타임에 동적으로 변경 가능하다는 점입니다. 이러한 특징 때문에 런타임에서는 값이 힙 영역에 저장됩니다.
6969

7070

7171
---

src/SUMMARY.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,8 @@
5959
- [rayon](./ch13-03.md)
6060
- [테스트](./ch14-00.md)
6161
- [유닛 테스트](./ch14-01.md)
62-
- [소스코드 살펴보기](./ch14-02.md)
63-
- [문서 테스트](./ch14-03.md)
64-
- [모킹](./ch14-04.md)
62+
- [문서 테스트](./ch14-02.md)
63+
- [모킹](./ch14-03.md)
6564
- [파이썬 바인딩](./ch15-00.md)
6665
- [파이썬 가상환경 만들기](./ch15-01.md)
6766
- [PyO3 프로젝트 생성하기](./ch15-02.md)

src/ch14-00.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,26 @@
1-
# CH14. 테스트
1+
# CH14. 테스트
2+
3+
4+
5+
## 프로그래밍에서 테스트가 필요한 이유
6+
7+
테스트는 소프트웨어 제품이 요구 사항을 충족하고 예상대로 작동하는지 확인하는 데 도움이 되므로 소프트웨어 개발의 필수적인 부분입니다. 테스트가 필요하고 중요한 이유는 여러 가지가 있습니다:
8+
9+
- 소프트웨어 적응성을 확인하기 위해: 테스트는 소프트웨어가 다양한 환경과 다양한 구성에서 올바르게 작동할 수 있는지 확인합니다.
10+
- 오류 식별: 테스트는 소프트웨어의 버그와 오류를 식별하여 제품 출시 전에 수정할 수 있도록 도와줍니다.
11+
- 추가 비용 방지: 개발 프로세스 초기에 버그를 발견하고 수정하면 출시 후에 버그를 발견하는 것보다 시간과 비용을 절약할 수 있습니다.
12+
- 소프트웨어 개발 가속화: 테스트를 통해 코드 변경 사항에 대한 빠른 피드백을 제공함으로써 개발 속도를 높일 수 있습니다.
13+
- 위험 방지: 테스트는 소프트웨어 제품 사용과 관련된 잠재적 위험을 식별하는 데 도움이 됩니다.
14+
15+
## 테스트의 종류
16+
17+
단위 테스트(unit test)와 통합 테스트(integration test)는 서로 다른 목적을 가진 두 가지 테스트 유형입니다.
18+
19+
단위 테스트는 고립된 작은 코드 조각이 의도한 대로 작동하는지 확인하기 위한 자동화된 테스트의 한 유형입니다. 일반적으로 개발자가 개발 중에 수행하며 함수나 프로시저와 같은 개별 소프트웨어 구성 요소에 중점을 둡니다.
20+
21+
반면에 통합 테스트는 여러 소프트웨어 구성 요소가 함께 작동하는 방식을 테스트하는 프로세스입니다. 통합 테스트는 통합된 유닛 간의 인터페이스가 올바른지 확인하는 데 중점을 둡니다. 통합 테스트는 일반적으로 단위 테스트가 완료된 후 시스템 테스트 전에 수행됩니다.
22+
23+
단위 테스트와 통합 테스트의 주요 차이점은 단위 테스트는 개별 코드 단위에 초점을 맞추고 통합 테스트는 이들이 함께 작동하는 방식에 초점을 맞춘다는 점입니다. 단위 테스트는 개발자가 코드가 원자 수준에서 올바르게 작동하는지 확인하는 데 도움이 되며, 통합 테스트는 시스템의 여러 부분이 올바르게 함께 작동하는지 확인하는 데 도움이 됩니다.
24+
25+
26+

src/ch14-01.md

Lines changed: 277 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,91 @@
11

2-
## 유닛 테스트
32

4-
유닛 테스트란,
53

6-
### 파이썬
4+
5+
## 유닛 테스트
6+
7+
### 소스코드 살펴보기
78

89
파이썬에서 유닛테스트를 위해서 사용하는 라이브러리로, 내장 라이브러리인 `unittest` 가 있습니다. 하지만 실제 현업에서는 일반적으로 `pytest` 패키지를 많이 사용합니다. 아래 명령어로 패키지를 설치합니다.
910

1011
```bash
1112
pip install pytest
1213
```
1314

15+
16+
17+
먼저 파이썬 코드를 살펴보겠습니다. `logic.py` 모듈은 다음과 같습니다.
18+
19+
```python
20+
from typing import Optional
21+
22+
CARDS = ["Rock", "Scissors", "Paper"]
23+
24+
25+
def play(card1: str, card2: str) -> Optional[bool]:
26+
assert card1 in CARDS, "Invalid card1"
27+
assert card2 in CARDS, "Invalid card2"
28+
29+
if card1 == card2:
30+
return None
31+
32+
if (
33+
(card1 == "Rock" and card2 == "Scissors")
34+
or (card1 == "Scissors" and card2 == "Paper")
35+
or (card1 == "Paper" and card2 == "Rock")
36+
):
37+
return True
38+
else:
39+
return False
40+
41+
42+
def stop():
43+
raise Exception("stop!")
44+
45+
```
46+
47+
`play()` 함수는 입력받은 두 카드의 값을 비교해 첫 번째 카드의 승패 유무를 리턴하는 함수입니다. 가위바위보에서 이기면 `True`, 지면 `False` , 비기면 `None` 을 리턴합니다. `stop()` 함수는 무조건 에러를 발생시켜 프로그램을 종료시킵니다.
48+
49+
이제 `test.py` 모듈을 보겠습니다. `@pytest.mark.parametrize` 는 테스트의 각 파라미터를 테스트 수행 중에 동적으로 넣을 수 있는 데코레이터입니다. 여기서 승, 무, 패 3가지를 테스트하는 함수 `test_win`, `test_draw`, `test_lose` 와 함께 함수 `stop()` 이 에러를 발생시키는지를 검사하는 `test_stop` 까지 총 4개의 테스트가 존재합니다.
50+
51+
```python
52+
import pytest
53+
54+
from logic import play, stop
55+
56+
57+
@pytest.mark.parametrize(
58+
"card1, card2",
59+
[("Rock", "Scissors"), ("Scissors", "Paper"), ("Paper", "Rock")],
60+
)
61+
def test_win(card1, card2):
62+
assert play(card1, card2) == True
63+
64+
65+
@pytest.mark.parametrize(
66+
"card1, card2",
67+
[("Rock", "Rock"), ("Scissors", "Scissors"), ("Paper", "Paper")],
68+
)
69+
def test_draw(card1, card2):
70+
assert play(card1, card2) == None
71+
72+
73+
@pytest.mark.parametrize(
74+
"card1, card2",
75+
[("Scissors", "Rock"), ("Rock", "Paper"), ("Paper", "Scissors")],
76+
)
77+
def test_lose(card1, card2):
78+
assert play(card1, card2) == False
79+
80+
81+
def test_stop():
82+
with pytest.raises(Exception) as exc:
83+
stop()
84+
85+
```
86+
87+
여기에 승무패 테스트에는 파라미터가 3종류씩 들어가므로 총 10개의 테스트가 수행되게 됩니다.
88+
1489
`pytest` 에는 정말 다양한 사용법이 존재하지만, 여기서는 기본적으로 테스트 모듈을 실행하는 것만 해보겠습니다. 현재 파이썬 폴더 밑에 `test.py` 파일에 정의된 테스트들을 수행해줍니다.
1590

1691
```bash
@@ -33,11 +108,81 @@ test.py .......... [100%]
33108

34109

35110

36-
### 러스트
111+
112+
113+
러스트 코드도 살펴봅시다. 여기서는 크레이트 루트로 라이브러리 크레이트를 사용합니다.
114+
115+
```rust,ignore
116+
#[derive(PartialEq)]
117+
pub enum Cards {
118+
Rock,
119+
Scissors,
120+
Paper,
121+
}
122+
123+
/// Demonstrate Rock, Scissors, Paper
124+
///
125+
/// ```
126+
/// use rust_part::{play, Cards};
127+
///
128+
/// let result = play(Cards::Rock, Cards::Scissors);
129+
/// assert_eq!(result, Some(true));
130+
/// ```
131+
pub fn play(card1: Cards, card2: Cards) -> Option<bool> {
132+
if card1 == card2 {
133+
return None;
134+
}
135+
match (card1, card2) {
136+
(Cards::Rock, Cards::Scissors) => Some(true),
137+
(Cards::Scissors, Cards::Paper) => Some(true),
138+
(Cards::Paper, Cards::Rock) => Some(true),
139+
_ => Some(false),
140+
}
141+
}
142+
143+
pub fn stop() {
144+
panic!("stop!");
145+
}
146+
147+
#[cfg(test)]
148+
pub mod test {
149+
// import everything in this module
150+
use super::*;
151+
152+
// No parametrized tests out of the box in Rust.
153+
#[test]
154+
fn test_win() {
155+
assert_eq!(play(Cards::Paper, Cards::Rock), Some(true));
156+
assert_eq!(play(Cards::Scissors, Cards::Paper), Some(true));
157+
assert_eq!(play(Cards::Paper, Cards::Rock), Some(true));
158+
}
159+
#[test]
160+
fn test_draw() {
161+
assert_eq!(play(Cards::Rock, Cards::Rock), None);
162+
assert_eq!(play(Cards::Scissors, Cards::Scissors), None);
163+
assert_eq!(play(Cards::Paper, Cards::Paper), None);
164+
}
165+
#[test]
166+
fn test_lose() {
167+
assert_eq!(play(Cards::Rock, Cards::Paper), Some(false));
168+
assert_eq!(play(Cards::Paper, Cards::Scissors), Some(false));
169+
assert_eq!(play(Cards::Scissors, Cards::Rock), Some(false));
170+
}
171+
172+
#[test]
173+
#[should_panic(expected="stop!")]
174+
fn test_stop(){
175+
stop();
176+
}
177+
}
178+
179+
```
180+
181+
37182

38183
`cargo` 에 내장된 test 러너로 유닛 테스트 실행이 가능합니다. 러스트는 테스트 파일을 별도로 만들지 않고, 같은 파일 안에 `test` 모듈을 넣어서 작성합니다. 이렇게 하면 테스트 모듈에서 대상 모듈에 대한 접근이 쉬워집니다. 다시 말해, private으로 선언된 함수에도 접근할 수 있기 때문에 테스트가 용이해집니다.
39184

40-
아래 명령어로 `lib.rs` 에 정의된 테스트 모듈의 테스트들을 수행합니다.
185+
아래 명령어로 테스트 모듈의 테스트들을 수행합니다.
41186

42187
```bash
43188
cargo test
@@ -68,4 +213,131 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
68213

69214
러스트는 테스트를 위해 바이너리를 빌드하는 과정이 먼저 수행됩니다. 그리고 유닛 테스트를 수행합니다. 마지막에 "Doc-tests"라는게 추가로 수행되는데 이는 밑에서 더 자세히 설명하겠습니다.
70215

216+
비슷하게 클래스도 테스트할 수 있습니다. 먼저 파이썬에서 다음과 같은 클래스를 정의합니다.
217+
218+
```python
219+
class Person:
220+
def __init__(self, name, age):
221+
self.name = name
222+
self._age = age
223+
224+
@property
225+
def age(self):
226+
return self._age
227+
228+
def hi(self):
229+
return f"Hi, I'm {self.name}, I'm {self._age} years old."
230+
231+
```
232+
233+
테스트 모듈에서는 객체화를 한 다음 프로퍼티와 메소드가 잘 적용되는지를 테스트합니다.
234+
235+
```python
236+
def test_hi():
237+
name = "John"
238+
age = 30
239+
person = Person(name, age)
240+
assert person.hi() == f"Hi, I'm {name}, I'm {age} years old."
241+
assert person.hi() == f"Hi, I'm {person.name}, I'm {person.age} years old."
242+
243+
```
244+
245+
러스트에서는 다음과 같이 구조체를 선언했습니다. 먼저 `person` 모듈을 선언하고 그 다음 `Person` 구조체와 메소드를 정의했습니다. 여기서 별도로 모듈을 만들지 않아도 상관없습니다.
246+
247+
```rust,ignore
248+
pub mod person {
249+
250+
pub struct Person {
251+
pub name: String,
252+
age: u8,
253+
}
254+
255+
impl Person {
256+
pub fn new(name: &str, age: u8) -> Person {
257+
Person {
258+
name: name.to_string(),
259+
age: age,
260+
}
261+
}
262+
263+
pub fn hi(&self) -> String {
264+
format!("Hi, I'm {}, I am {} years old.", self.name, self.age())
265+
}
266+
267+
pub fn age(&self) -> u8 {
268+
self.age
269+
}
270+
}
271+
}
272+
273+
```
274+
275+
그 다음 테스트 모듈에 아래 함수를 추가합니다.
276+
277+
```rust,ignore
278+
#[test]
279+
fn test_hi() {
280+
let name = "John";
281+
let age: u8 = 30;
282+
let person = person::Person::new(name, age);
283+
assert_eq!(
284+
person.hi(),
285+
format!("Hi, I'm {}, I am {} years old.", name, age)
286+
);
287+
assert_eq!(
288+
person.hi(),
289+
format!("Hi, I'm {}, I am {} years old.", person.name, person.age())
290+
);
291+
}
292+
```
293+
294+
파이썬과 마찬가지로 프로퍼티와 메소드가 잘 적용되는지를 테스트합니다. 아래 명령어로 테스트들을 수행합니다.
295+
296+
```bash
297+
cargo test
298+
```
299+
300+
301+
### 비동기 함수 테스트
302+
303+
`#[tokio::test]`를 함수에 붙여주면 됩니다.
304+
305+
306+
307+
```rust
308+
async fn give_order(order: u64) -> u64 {
309+
println!("Processing {order}...");
310+
tokio::time::sleep(std::time::Duration::from_secs(3 - order)).await;
311+
println!("Finished {order}");
312+
order
313+
}
314+
315+
#[tokio::main]
316+
async fn main() {
317+
let result = tokio::join!(give_order(1), give_order(2), give_order(3));
318+
319+
println!("{:?}", result);
320+
}
321+
322+
#[cfg(test)]
323+
mod tests {
324+
use super::*;
325+
326+
#[tokio::test]
327+
async fn test_give_order() {
328+
let result = give_order(1).await;
329+
assert_eq!(result, 1);
330+
}
331+
}
332+
333+
```
334+
335+
실행 결과
336+
337+
```
338+
running 1 test
339+
test tests::test_give_order ... ok
340+
341+
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.00s
342+
```
71343

0 commit comments

Comments
 (0)