1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
//! Mocking utility actor
//! This actor wraps any actor, and replaces instances of that actor with
//! mocker actor, which is able to accept all messages which the actor can
//! receive.
//!
//! Mocking is intended to be achieved by using a pattern similar to
//! ```rust,ignore
//! #[cfg(not(test))]
//! type DBClientAct = DBClientActor;
//! #[cfg(test)]
//! type DBClientAct = Mocker<DBClientActor>;
//! ```
//! Then, the actor should be used as a System Service (or Arbiter Service, but
//! take care that all the places which will use the mocked actor is on the
//! same arbiter). Thus, in a test, it will retrieve the mocker from the
//! registry instead of the actual actor.
//!
//! To set the mock function in the actor, the init_actor function is used,
//! which allows the state of an actor to be set when it is started as a
//! arbiter or system service. A closure which takes Box<Any> is evaluated for
//! every message, and must return Box<Any>, specifically the return type
//! for the message type send.
//!
//! See the mock example to see how it can be used.
use std::any::Any;
use std::marker::PhantomData;
use std::mem;
use handler::MessageResponse;
use prelude::*;
/// This actor is able to wrap another actor and accept all the messages the
/// wrapped actor can, passing it to a closure which can mock the response of
/// the actors
pub struct Mocker<T: Sized + 'static> {
phantom: PhantomData<T>,
mock: Box<FnMut(Box<Any>, &mut Context<Mocker<T>>) -> Box<Any>>,
}
impl<T> Mocker<T> {
pub fn mock(
mock: Box<FnMut(Box<Any>, &mut Context<Mocker<T>>) -> Box<Any>>,
) -> Mocker<T> {
Mocker::<T> {
phantom: PhantomData,
mock,
}
}
}
impl<T: SystemService> actix::SystemService for Mocker<T> {}
impl<T: ArbiterService> actix::ArbiterService for Mocker<T> {}
impl<T> Supervised for Mocker<T> {}
impl<T> Default for Mocker<T> {
fn default() -> Self {
panic!("Mocker actor used before set")
}
}
impl<T: Sized + 'static> Actor for Mocker<T> {
type Context = Context<Self>;
}
impl<M: 'static, T: Sized + 'static> Handler<M> for Mocker<T>
where
M: Message,
<M as actix::Message>::Result: MessageResponse<Mocker<T>, M>,
{
type Result = M::Result;
fn handle(&mut self, msg: M, ctx: &mut Self::Context) -> M::Result {
let mut ret = (self.mock)(Box::new(msg), ctx);
let out = mem::replace(
ret.downcast_mut::<Option<M::Result>>()
.expect("wrong return type for message"),
None,
);
match out {
Some(a) => a,
_ => panic!(),
}
}
}