๋ชจ์˜ ์ฒ˜๋ฆฌ

๋ชจ์˜ ์ฒ˜๋ฆฌ์˜ ๊ฒฝ์šฐ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ Mockall์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํŠธ๋ ˆ์ž‡์„ ์‚ฌ์šฉํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ๋ฆฌํŒฉํ„ฐ๋งํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋น ๋ฅด๊ฒŒ ๋ชจ์˜ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

use std::time::Duration;

#[mockall::automock]
pub trait Pet {
    fn is_hungry(&self, since_last_meal: Duration) -> bool;
}

#[test]
fn test_robot_dog() {
    let mut mock_dog = MockPet::new();
    mock_dog.expect_is_hungry().return_const(true);
    assert_eq!(mock_dog.is_hungry(Duration::from_secs(10)), true);
}
This slide should take about 5 minutes.
  • Mockall is the recommended mocking library in Android (AOSP). There are other mocking libraries available on crates.io, in particular in the area of mocking HTTP services. The other mocking libraries work in a similar fashion as Mockall, meaning that they make it easy to get a mock implementation of a given trait.

  • ๋ชจ์˜ ์ฒ˜๋ฆฌ๋Š” ๋‹ค์†Œ ๋…ผ๋ž€์˜ ์—ฌ์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ์˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ…Œ์ŠคํŠธ๋ฅผ ์ข…์† ํ•ญ๋ชฉ์—์„œ ์™„์ „ํžˆ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ ํ…Œ์ŠคํŠธ ์‹คํ–‰์ด ๋”์šฑ ๋น ๋ฅด๊ณ  ์•ˆ์ •์ ์œผ๋กœ ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด์— ๋ชจ์˜๋Š” ์ž˜๋ชป ๊ตฌ์„ฑ๋˜์–ด ์‹ค์ œ ์ข…์† ํ•ญ๋ชฉ์ด ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ๊ณผ ๋‹ค๋ฅธ ์ถœ๋ ฅ์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    ๊ฐ€๋Šฅํ•˜๋ฉด ์‹ค์ œ ์ข…์† ํ•ญ๋ชฉ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋‚ด ๋ฐฑ์—”๋“œ๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ํ…Œ์ŠคํŠธ์—์„œ ์˜ฌ๋ฐ”๋ฅธ ๋™์ž‘์„ ๊ฐ€์ ธ์˜ฌ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์†๋„๊ฐ€ ๋น ๋ฅด๊ณ  ์ž๋™์œผ๋กœ ์ •๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

    ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋งŽ์€ ์›น ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋„ localhost์˜ ์ž„์˜ ํฌํŠธ์— ๋ฐ”์ธ๋”ฉ๋˜๋Š” ํ”„๋กœ์„ธ์Šค ๋‚ด ์„œ๋ฒ„๋ฅผ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋ชจ์˜ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค๋Š” ํ•ญ์ƒ ์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ํ™˜๊ฒฝ์—์„œ ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

  • Mockall์€ Rust ํ”Œ๋ ˆ์ด๊ทธ๋ผ์šด๋“œ์˜ ์ผ๋ถ€๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ ์ด ์˜ˆ๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Mockall์„ ๊ธฐ์กด Cargo ํ”„๋กœ์ ํŠธ์— ๋น ๋ฅด๊ฒŒ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด cargo add mockall์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • Mockall์—๋Š” ๋” ๋งŽ์€ ๊ธฐ๋Šฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ์ „๋‹ฌ๋œ ์ธ์ˆ˜์— ๋”ฐ๋ผ ๊ธฐ๋Œ€์น˜๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๋งˆ์ง€๋ง‰์œผ๋กœ ๋จน์ด๋ฅผ ๋จน๊ณ  3์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ๋ฐฐ๊ณ ํŒŒ์ง€๋Š” ๊ณ ์–‘์ด๋ฅผ ๋ชจ์˜ํ•˜๋Š” ๋ฐ ์ด๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

#[test]
fn test_robot_cat() {
    let mut mock_cat = MockPet::new();
    mock_cat
        .expect_is_hungry()
        .with(mockall::predicate::gt(Duration::from_secs(3 * 3600)))
        .return_const(true);
    mock_cat.expect_is_hungry().return_const(false);
    assert_eq!(mock_cat.is_hungry(Duration::from_secs(1 * 3600)), false);
    assert_eq!(mock_cat.is_hungry(Duration::from_secs(5 * 3600)), true);
}
  • .times(n)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ๋Š” ํšŸ์ˆ˜๋ฅผ n์œผ๋กœ ์ œํ•œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์กฐ๊ฑด์ด ์ถฉ์กฑ๋˜์ง€ ์•Š์œผ๋ฉด ๋ชจ์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ์‚ญ์ œ๋  ๋•Œ ์ž๋™์œผ๋กœ ํŒจ๋‹‰ ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.