Skip to content

Instantly share code, notes, and snippets.

@dialektike
Created August 7, 2021 05:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dialektike/a2b76f5b5a1ac305868a89061a5b99d6 to your computer and use it in GitHub Desktop.
Save dialektike/a2b76f5b5a1ac305868a89061a5b99d6 to your computer and use it in GitHub Desktop.
자동화된 테스트 작정하기(Writing Automated Tests)

자동화된 테스트 작정하기(Writing Automated Tests)

테스트를 작정하는 방법

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn larger_can_hold_smaller() {
        let larger = Rectangle {
            width: 8,
            height: 7,
        };
        let smaller = Rectangle {
            width: 5,
            height: 1,
        };

        assert!(larger.can_hold(&smaller));
    }
}

윗 코드를 테스트 합니다.

❯ cargo test
   Compiling adder v0.1.0 (/Users/jaehwan/git/rust/projects/adder)
warning: associated function is never used: `can_hold`
 --> src/lib.rs:8:8
  |
8 |     fn can_hold(&self, other: &Rectangle) -> bool {
  |        ^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: 1 warning emitted

    Finished test [unoptimized + debuginfo] target(s) in 0.34s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 1 test
test tests::larger_can_hold_smaller ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

테스트 성공! 테스트 코드를 하나 더 추가합니다.

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn larger_can_hold_smaller() {
        let larger = Rectangle {
            width: 8,
            height: 7,
        };
        let smaller = Rectangle {
            width: 5,
            height: 1,
        };

        assert!(larger.can_hold(&smaller));
    }

        #[test]
    fn smaller_cannot_hold_larger() {
        let larger = Rectangle {
            width: 8,
            height: 7,
        };
        let smaller = Rectangle {
            width: 5,
            height: 1,
        };

        assert!(!smaller.can_hold(&larger));
    }
}

결과는 다음과 같습니다.

❯ cargo test
   Compiling adder v0.1.0 (/Users/jaehwan/git/rust/projects/adder)
warning: associated function is never used: `can_hold`
 --> src/lib.rs:8:8
  |
8 |     fn can_hold(&self, other: &Rectangle) -> bool {
  |        ^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: 1 warning emitted

    Finished test [unoptimized + debuginfo] target(s) in 0.34s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 2 tests
test tests::larger_can_hold_smaller ... ok
test tests::smaller_cannot_hold_larger ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

두개 모두 통과!

코드를 변경해 봅시다.

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width < other.width && self.height > other.height
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn larger_can_hold_smaller() {
        let larger = Rectangle {
            width: 8,
            height: 7,
        };
        let smaller = Rectangle {
            width: 5,
            height: 1,
        };

        assert!(larger.can_hold(&smaller));
    }

        #[test]
    fn smaller_cannot_hold_larger() {
        let larger = Rectangle {
            width: 8,
            height: 7,
        };
        let smaller = Rectangle {
            width: 5,
            height: 1,
        };

        assert!(!smaller.can_hold(&larger));
    }
}

결과는 다음과 같습니다.

❯ cargo test
   Compiling adder v0.1.0 (/Users/jaehwan/git/rust/projects/adder)
warning: associated function is never used: `can_hold`
 --> src/lib.rs:8:8
  |
8 |     fn can_hold(&self, other: &Rectangle) -> bool {
  |        ^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: 1 warning emitted

    Finished test [unoptimized + debuginfo] target(s) in 0.34s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 2 tests
test tests::smaller_cannot_hold_larger ... ok
test tests::larger_can_hold_smaller ... FAILED

failures:

---- tests::larger_can_hold_smaller stdout ----
thread 'tests::larger_can_hold_smaller' panicked at 'assertion failed: larger.can_hold(&smaller)', src/lib.rs:28:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::larger_can_hold_smaller

test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'

이번에는 에러가 발생했습니다.

assert_eq!assert_ne! 메크로를 이용한 동등성(Equality) 테스트

A common way to test functionality is to compare the result of the code under test to the value you expect the code to return to make sure they’re equal.

pub fn add_two(a: i32) -> i32 {
    a + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_adds_two() {
        assert_eq!(4, add_two(2));
    }
}

결과는 다음과 같습니다.

❯ cargo test
   Compiling adder v0.1.0 (/Users/jaehwan/git/rust/projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.33s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 1 test
test tests::it_adds_two ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

assert_eq!(4, add_two(2))을 통해서 add_two(2)의 결과가 4와 같기 때문에 테스트는 성공했습니다. 자 다음과 같이 변경합니다. a + 3으로 코드를 변경했습니다.

pub fn add_two(a: i32) -> i32 {
    a + 3
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_adds_two() {
        assert_eq!(4, add_two(2));
    }
}

결과는 다음과 같습니다.

❯ cargo test
   Compiling adder v0.1.0 (/Users/jaehwan/git/rust/projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.34s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 1 test
test tests::it_adds_two ... FAILED

failures:

---- tests::it_adds_two stdout ----
thread 'tests::it_adds_two' panicked at 'assertion failed: `(left == right)`
  left: `4`,
 right: `5`', src/lib.rs:11:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::it_adds_two

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'

당연히 실패! 아래와 같이 assert_eq!(add_two(2), 4)으로 변경해 봅시다.

pub fn add_two(a: i32) -> i32 {
    a + 3
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_adds_two() {
        assert_eq!(add_two(2), 4);
    }
}

결과는 다음과 같습니다.

❯ cargo test
   Compiling adder v0.1.0 (/Users/jaehwan/git/rust/projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.33s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 1 test
test tests::it_adds_two ... FAILED

failures:

---- tests::it_adds_two stdout ----
thread 'tests::it_adds_two' panicked at 'assertion failed: `(left == right)`
  left: `5`,
 right: `4`', src/lib.rs:11:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::it_adds_two

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'

당연히 실패죠. 여기서 참고할 점은 러스트는 assert_eq!(add_two(2), 5)assert_eq!(5, add_two(2), 5) 같이 값을 변경해도, 단지 오른쪽, 왼쪽 문제이기 때문에 글 순서가 중요하지 않습니다. 그래서 아래와 같이 순서를 변경하더라고 테스트가 성공하는데는 문제가 없습니다.

pub fn add_two(a: i32) -> i32 {
    a + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_adds_two() {
        assert_eq!(add_two(2), 4);
    }
}

assert_ne!은 값이 다르면 테스트를 통과한 것입니다. 다음 코드를 봅시다.

pub fn add_two(a: i32) -> i32 {
    a + 3
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_adds_two() {
        assert_ne!(4, add_two(2));
    }
}

결과는 다음과 같습니다.

❯ cargo test
   Compiling adder v0.1.0 (/Users/jaehwan/git/rust/projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.34s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 1 test
test tests::it_adds_two ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

테스트를 통과했습니다. assert_eq!assert_ne! 메크로는 내부적으로 연산자 == and !=을 사용합니다.

Adding Custom Failure Messages

이번에는 특정 문자열, 아래 예에서는 Carol이 들어있는지, 테스트합니다.

pub fn greeting(name: &str) -> String {
    format!("Hello {}!", name)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn greeting_contains_name() {
        let result = greeting("Carol");
        assert!(result.contains("Carol"));
    }
}

결과는 다음과 같고 당연히 테스트를 통과할 것입니다.

❯ cargo test
   Compiling adder v0.1.0 (/Users/jaehwan/git/rust/projects/adder)
    Finished test [unoptimized + debuginfo] target(s) in 0.35s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 1 test
test tests::greeting_contains_name ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

반환 문자열에 "Carol"이 안 들어가도록 위의 코드, format!("Hello {}!", name)String::from("Hello {}!")으로 다음과 같이 바꾸고 테스트를 해봅시다.

pub fn greeting(name: &str) -> String {
    String::from("Hello {}!")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn greeting_contains_name() {
        let result = greeting("Carol");
        assert!(result.contains("Carol"));
    }
}

이것은 당연히 통과하지 못할 것입니다.

❯ cargo test
   Compiling adder v0.1.0 (/Users/jaehwan/git/rust/projects/adder)
warning: unused variable: `name`
 --> src/lib.rs:1:17
  |
1 | pub fn greeting(name: &str) -> String {
  |                 ^^^^ help: if this is intentional, prefix it with an underscore: `_name`
  |
  = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `name`
 --> src/lib.rs:1:17
  |
1 | pub fn greeting(name: &str) -> String {
  |                 ^^^^ help: if this is intentional, prefix it with an underscore: `_name`
  |
  = note: `#[warn(unused_variables)]` on by default

warning: 1 warning emitted

warning: 1 warning emitted

    Finished test [unoptimized + debuginfo] target(s) in 0.39s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 1 test
test tests::greeting_contains_name ... FAILED

failures:

---- tests::greeting_contains_name stdout ----
thread 'tests::greeting_contains_name' panicked at 'assertion failed: result.contains(\"Carol\")', src/lib.rs:12:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::greeting_contains_name

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'

윗 코드를 조금 더 변경해서 뭐가 잘 못 되었는지 우리가 작성한 메세지도 나오고 결괏값도 나올 수 있게 해보자.

pub fn greeting(name: &str) -> String {
    String::from("Hello {}!")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn greeting_contains_name() {
        let result = greeting("Carol");
        assert!(
            result.contains("Carol"),
            "greeting()의 반환값이 Carol을 포함하고 있지 않습니다. \
            결과값은: {}", result);
    }
}

결과는 다음과 같습니다.

❯ cargo test
   Compiling adder v0.1.0 (/Users/jaehwan/git/rust/projects/adder)
warning: unused variable: `name`
 --> src/lib.rs:1:17
  |
1 | pub fn greeting(name: &str) -> String {
  |                 ^^^^ help: if this is intentional, prefix it with an underscore: `_name`
  |
  = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `name`
 --> src/lib.rs:1:17
  |
1 | pub fn greeting(name: &str) -> String {
  |                 ^^^^ help: if this is intentional, prefix it with an underscore: `_name`
  |
  = note: `#[warn(unused_variables)]` on by default

warning: 1 warning emitted

warning: 1 warning emitted

    Finished test [unoptimized + debuginfo] target(s) in 0.40s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 1 test
test tests::greeting_contains_name ... FAILED

failures:

---- tests::greeting_contains_name stdout ----
thread 'tests::greeting_contains_name' panicked at 'greeting()의 반환값이 Carol을 포함하고 있지 않습니다. 결과값은: Hello {}!', src/lib.rs:12:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::greeting_contains_name

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Checking for Panics with should_panic

#[should_panic]을 사용하면 함수 내의 코드에서 패닉이 발생하면 테스트에서 성공하고, 패닉이 발생하지 않으면, 테스트에 실패하게 된다. 아래 코드는 당연히 패닉이 발생하는 코드다. Guess()에는 1보다 크고 100보다 작은 값을 넣어야 하는데 200을 넣었기 때문이다.

pub struct Guess {
    value: i32,
}

impl Guess {
    pub fn new(value: i32) -> Guess {
        if value < 1 || value > 100 {
            panic!("Guess value는 1과 100값을 사용해야 하는데 \
            현재 값은 {}.", value);
        }

        Guess { value }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic]
    fn greater_than_100() {
        Guess::new(200);
    }
}

결과는 아래와 같습니다.

❯ cargo test
   Compiling adder v0.1.0 (/Users/jaehwan/git/rust/projects/adder)
warning: field is never read: `value`
 --> src/lib.rs:2:5
  |
2 |     value: i32,
  |     ^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: field is never read: `value`
 --> src/lib.rs:2:5
  |
2 |     value: i32,
  |     ^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: 1 warning emitted

warning: 1 warning emitted

    Finished test [unoptimized + debuginfo] target(s) in 0.32s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 1 test
test tests::greater_than_100 ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

당연히 테스트를 통과합니다. 이제는 통과하지 못하게 해보겠습니다.

pub struct Guess {
    value: i32,
}

impl Guess {
    pub fn new(value: i32) -> Guess {
        if value < 1 {
            panic!("Guess value는 1과 100값을 사용해야 하는데 \
            현재 값은 {}.", value);
        }

        Guess { value }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic]
    fn greater_than_100() {
        Guess::new(200);
    }
}

결과는 다음과 같습니다.

❯ cargo test
warning: field is never read: `value`
 --> src/lib.rs:2:5
  |
2 |     value: i32,
  |     ^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: 1 warning emitted

warning: field is never read: `value`
 --> src/lib.rs:2:5
  |
2 |     value: i32,
  |     ^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: 1 warning emitted

    Finished test [unoptimized + debuginfo] target(s) in 0.00s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 1 test
test tests::greater_than_100 ... FAILED

failures:

---- tests::greater_than_100 stdout ----
note: test did not panic as expected

failures:
    tests::greater_than_100

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'

역시 테스트를 통과하지 못 합니다. 그러나 문제는 should_panic이 의도한 것과는 다른 것으로 페닉이 발생하여 테스트를 통과하는 경우가 있다는 점입니다. 이를 확인하기 위해서 should_panicexpected을 추가해서 어느 쪽에서 패닉이 발생하는지 확인할 수 있습니다. 우선 아래와 같이 코드를 수정합시다.

pub struct Guess {
    value: i32,
}

impl Guess {
    pub fn new(value: i32) -> Guess {
        if value < 1 {
            panic!("Guess value는 1보다 크거나 같은 값을 사용해야 합니다. \
            현재 값은 {}.", value);
        } else if value > 100 {
            panic!("Guess value는 100보다 작거나 같은 값을 사용해야 합니다. \
            현재 값은 {}.", value);
        }

        Guess { value }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic(expected ="\
    Guess value는 100보다 작거나 같은 값을 사용해야 합니다.")]
    fn greater_than_100() {
        Guess::new(200);
    }
}

결과는 다음과 같습니다.

❯ cargo test
   Compiling adder v0.1.0 (/Users/jaehwan/git/rust/projects/adder)
warning: field is never read: `value`
 --> src/lib.rs:2:5
  |
2 |     value: i32,
  |     ^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: field is never read: `value`
 --> src/lib.rs:2:5
  |
2 |     value: i32,
  |     ^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: 1 warning emitted

warning: 1 warning emitted

    Finished test [unoptimized + debuginfo] target(s) in 0.32s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 1 test
test tests::greater_than_100 ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

예상한 패닉이 발생하여, 그 패닉을 통해서 나온 메세지가 expected ="Guess value는 100보다 작거나 같은 값을 사용해야 합니다."과 동일했기 때문에 테스트를 통과했습니다. 그러나 아래와 같이 메세지를 바꾸면 당연히 테스트를 통과할 수 없습니다. 예상한 메세지가 안 나왔기 때문입니다.

pub struct Guess {
    value: i32,
}

impl Guess {
    pub fn new(value: i32) -> Guess {
        if value < 1 {
            panic!("Guess value는 100보다 작거나 같은 값을 사용해야 합니다. \
            현재 값은 {}.", value);
        } else if value > 100 {
            panic!("Guess value는 1보다 크거나 같은 값을 사용해야 합니다. \
            현재 값은 {}.", value);
        }

        Guess { value }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic(expected ="\
    Guess value는 100보다 작거나 같은 값을 사용해야 합니다.")]
    fn greater_than_100() {
        Guess::new(200);
    }
}

결돠는 다음과 같습니다.

❯ cargo test
   Compiling adder v0.1.0 (/Users/jaehwan/git/rust/projects/adder)
warning: field is never read: `value`
 --> src/lib.rs:2:5
  |
2 |     value: i32,
  |     ^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: field is never read: `value`
 --> src/lib.rs:2:5
  |
2 |     value: i32,
  |     ^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: 1 warning emitted

warning: 1 warning emitted

    Finished test [unoptimized + debuginfo] target(s) in 0.32s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 1 test
test tests::greater_than_100 ... FAILED

failures:

---- tests::greater_than_100 stdout ----
thread 'tests::greater_than_100' panicked at 'Guess value는 1보다 크거나 같은 값을 사용해야 합니다. 현재 값은 200.', src/lib.rs:11:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
note: panic did not contain expected string
      panic message: `"Guess value는 1보다 크거나 같은 값을 사용해야 합니다. 현재 값은 200."`,
 expected substring: `"Guess value는 100보다 작거나 같은 값을 사용해야 합니다."`

failures:
    tests::greater_than_100

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--lib'

다음과 같이 되돌리면, 패닉이 발생하여 다시 테스트를 통과합니다.

pub struct Guess {
    value: i32,
}

impl Guess {
    pub fn new(value: i32) -> Guess {
        if value < 1 {
            panic!("Guess value는 1보다 크거나 같은 값을 사용해야 합니다. \
            현재 값은 {}.", value);
        } else if value > 100 {
            panic!("Guess value는 100보다 작거나 같은 값을 사용해야 합니다. \
            현재 값은 {}.", value);
        }

        Guess { value }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic(expected ="\
    Guess value는 100보다 작거나 같은 값을 사용해야 합니다.")]
    fn greater_than_100() {
        Guess::new(200);
    }
}

결과는 다음과 같습니다.

❯ cargo test
   Compiling adder v0.1.0 (/Users/jaehwan/git/rust/projects/adder)
warning: field is never read: `value`
 --> src/lib.rs:2:5
  |
2 |     value: i32,
  |     ^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: field is never read: `value`
 --> src/lib.rs:2:5
  |
2 |     value: i32,
  |     ^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: 1 warning emitted

warning: 1 warning emitted

    Finished test [unoptimized + debuginfo] target(s) in 0.83s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 1 test
test tests::greater_than_100 ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

테스트 구성(Test Organization)

단위 테스트(Unit Tests)

The purpose of unit tests is to test each unit of code in isolation from the rest of the code to quickly pinpoint where code is and isn’t working as expected.

단위 테스트의 목적은 코드의 각 단위를 나머지 코드와는 독립적으로 테스트해서 코드가 의도대로 동작하는지를 빠르게 판단하는 것이다.

You’ll put unit tests in the src directory in each file with the code that they’re testing.

여러분은 src 디렉토리안의 단위 테스트를

Testing Private Functions

아래와 같이 pub으로 공개로 만들지 않아도 테스트를 할 수 있다.

pub fn add_two(a: i32) -> i32 {
    internal_adder(a, 2)
}

fn internal_adder(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn internal() {
        assert_eq!(4, internal_adder(2, 2));
    }
}

통합 테스트(Integration Tests)

In Rust, integration tests are entirely external to your library. 러스트에서, 통합 테스트는 전적으로 여러분의 라이브러리 밖에 있게 된다.

The tests Directory

lib.rs을 다음과 같이 작성한다.

pub fn add_two(a: i32) -> i32 {
    internal_adder(a, 2)
}

fn internal_adder(a: i32, b: i32) -> i32 {
    a + b
}

그런 다음 tests 디렉토리를 만든 다음 그 아래 integration_test.rs 파일은 만듭니다. 그러면 디렉토리 구조는 다음과 같이 됩니다.

├── src
│  ├── lib.rs
│  └── tests
│     └── integration_test.rs

integration_test.rs에 다음과 같은 코드를 작성합니다.

use adder;

#[test]
fn it_adds_two() {
    assert_eq!(4, adder::add_two(2));
}

실행 결과는 다음과 같습니다.

❯ cargo test
    Finished test [unoptimized + debuginfo] target(s) in 0.00s
     Running unittests (target/debug/deps/adder-ef498f7c831aab4c)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

tests 디렉토리에서 만든 파일을 가지고 테스트를 통합적으로 할 수 있습니다. 우리는 tests/integration_test.rs에서 #[cfg(test)]을 사용할 필요가 없습니다.

Cargo treats the tests directory specially and compiles files in this directory only when we run cargo test.

더 많은 파일을 만들면 많은 테스트를 할 수 있지만, 특정 한 파일만으로 테스트를 하려면 cargo test --test integration_test과 같이 하면 됩니다.

Submodules in Integration Tests

Integration Tests for Binary Crates

If our project is a binary crate that only contains a src/main.rs file and doesn’t have a src/lib.rs file, we can’t create integration tests in the tests directory and bring functions defined in the src/main.rs file into scope with a use statement.

src/main.rs 파일 안에 정의되어 있는 함수를 use 문을 사용하여, tests 디렉토리 안에서 '통합 테스트(integration tests)'를 생성할 수는 없다.

This is one of the reasons Rust projects that provide a binary have a straightforward src/main.rs file that calls logic that lives in the src/lib.rs file.

이것이 러스트 프로젝트에서 src/main.rs 파일에서 src/lib.rs 파일 안에 살고 있는 로직을 직접적으로(straightforward) 가지고 올 수 있는 이유 중의 하나입니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment