actix/actors/
mocker.rs

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