Для возврата MutexGuard из lazy_static Mutex из функции необходим параметр времени жизни - PullRequest
0 голосов
/ 26 мая 2018

Я пишу тесты, используя фиктивные функции, контролируя возвращаемое значение среди тестов с помощью Mutex:

#[macro_use]
extern crate lazy_static;

#[cfg(test)]
pub use mock::*;
#[cfg(not(test))]
pub use real::*;

mod real {
    pub fn say_hello(_name: String) -> String {
        unimplemented!()
    }
}

/// simulate multiple uses, replace `real` in test.
mod mock {
    use std::sync::*;
    lazy_static! {
        pub static ref LOCK: Mutex<bool> = Mutex::new(true);
        pub static ref HELLO_VALUE: Mutex<String> = Mutex::new(String::default());
    }
    pub fn say_hello(_name: String) -> String {
        use std::ops::Deref;
        HELLO_VALUE.lock().unwrap().deref().clone()
    }

    pub fn set_hello_return_value(rtn: String) -> MutexGuard<bool> {
        let lock = LOCK.lock().unwrap();
        let mut value = HELLO_VALUE.lock().unwrap();
        *value = rtn;
        lock
    }
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn test1() {
        // repeated block begin--------------------------
        let _lock = LOCK.lock().unwrap();
        let mut value = HELLO_VALUE.lock().unwrap();
        *value = "Hello Tom!".to_string(); // just this line is different from test2
        drop(value);
        // repeat block end--------------------------
        assert_eq!("Hello Tom!", say_hello("".to_string()));
    }

    #[test]
    fn test2() {
        // repeated block begin--------------------------
        let _lock = LOCK.lock().unwrap();
        let mut value = HELLO_VALUE.lock().unwrap();
        *value = "Hello Jack!".to_string(); // just this line is different from test1
        drop(value);
        // repeat block end--------------------------
        assert_eq!("Hello Jack!", say_hello("".to_string()));
    }

    #[test]
    fn test_simplified_but_not_work() {
        let _lock = set_hello_return_value("Hello Mark!".to_string());
        assert_eq!("Hello Mark!", say_hello("".to_string()));
    }
}

. Вы можете увидеть блок повторений , который я хочуупрощать.Я сделал функцию set_hello_return_value, но компилятор пожаловался:

error[E0106]: missing lifetime specifier
  --> src/main.rs:28:51
   |
28 |     pub fn set_hello_return_value(rtn: String) -> MutexGuard<bool> {
   |                                                   ^^^^^^^^^^^^^^^^ expected lifetime parameter
   |
   = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
   = help: consider giving it an explicit bounded or 'static lifetime

Пожалуйста, помогите мне исправить это.

1 Ответ

0 голосов
/ 26 мая 2018

Прочитайте полное сообщение об ошибке:

рассмотрите возможность предоставления ему явного ограниченного или «статического времени жизни»

Это работает так:

pub fn set_hello_return_value(rtn: String) -> MutexGuard<'static, bool> {
    let lock = LOCK.lock().unwrap();
    let mut value = HELLO_VALUE.lock().unwrap();
    *value = rtn;
    lock
}

Я бы, наверное, вообще не вернул охрану:

pub fn with_hello_return_value<S, F>(rtn: S, f: F)
where
    S: Into<String>,
    F: FnOnce(),
{
    let _lock = LOCK.lock().unwrap();
    *HELLO_VALUE.lock().unwrap() = rtn.into();
    f()
}

#[test]
fn test_simplified() {
    with_hello_return_value("Hello Mark!", || {
        assert_eq!("Hello Mark!", say_hello("".to_string()));
    });
}

Честно говоря, я бы не стал делать этого, поскольку условная компиляция излишня.Если вам нужно тестировать компоненты вашей системы отдельно, они не должны знать друг о друге для начала;они должны быть введены зависимостью.Это дает дополнительное преимущество, заключающееся в том, что каждый тест может вводить свое собственное значение, сохраняя многопоточный характер тестов.

fn thing_that_uses_say_hello<G>(greeter: &G, name: &str) -> String
where
    G: Greeting,
{
    greeter.say_hello(name.into())
}

trait Greeting {
    fn say_hello(&self, name: &str) -> String;
}

struct RealGreeting;

impl Greeting for RealGreeting {
    fn say_hello(&self, name: &str) -> String {
        format!("Hello, {}", name)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::cell::RefCell;

    struct MockGreeting<'a> {
        called_with: RefCell<Vec<String>>,
        value: &'a str,
    }

    impl<'a> MockGreeting<'a> {
        fn new(value: &'a str) -> Self {
            Self {
                value,
                called_with: Default::default(),
            }
        }
    }

    impl<'a> Greeting for MockGreeting<'a> {
        fn say_hello(&self, name: &str) -> String {
            self.called_with.borrow_mut().push(name.to_owned());
            self.value.to_owned()
        }
    }

    #[test]
    fn test1() {
        let g = MockGreeting::new("Hello");
        let r = thing_that_uses_say_hello(&g, "Tom");
        assert_eq!("Hello", r);
        assert_eq!(&*g.called_with.borrow(), &["Tom".to_string()]);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...