seal_rs/testkit/actors/mod.rs
1//! Test framework for actors
2//!
3//! # Table of Contents
4//! 1. [Introduction](#introduction)
5//! 2. [TestActorSystem](#testactorsystem)
6//! 3. [TestActorRef](#testactorref)
7//! 4. [TestProbe](#testprobe)
8//! 5. [Examples](#examples)
9//!
10//! # Introduction
11//!
12//! Amazing opportunity of the untyped actors consists in the feature, that actor may by tested in
13//! fully isolated test environment. All outside environment of the actor will be mocked, but he
14//! itself will be think, that it work in the real world system, and never will known about that
15//! all they environment is emulated. In order to achieve this effect, testkit use few elements.
16//! Let's look on they!
17//!
18//! # TestLocalActorSystem
19//!
20//! This is a special test realization of the actor system (actor system itself is abstract interface,
21//! and may have any realizations). She fully duplicate functional of the regular actor system, but
22//! add to her various functions for needs of testing. She may be created as regular actor system.
23//! Only difference consist in that this system, does not need run explicitly. She is run itself
24//! right after creation.
25//!
26//! ```
27//! let system = TestLocalActorSystem::new();
28//! ```
29//!
30//! # TestLocalActorRef
31//!
32//! Special ActorRef which produced by the TestLocalActorSystem. Like the last, he is fully
33//! duplicate functions of the regular reference with appending an additional functions.
34//!
35//! ### Access to the actor's state
36//!
37//! TestLocalActorRef allow you get direct access to the internal actor object and see it's state.
38//! This state is immutable, but may be used for assertions about states change after processing
39//! some test actions. Link to the actor is accessed as "actor" attribute of the reference object
40//! and represents as Any type object. For use it, you need downcast they to the concrete type.
41//! Because it is strongly complex operation, testkit present special macro for this:
42//!
43//! ```
44//! in_state! (target, SomeType, actor => {
45//! assert_eq!(actor.data, 599);
46//! });
47//! ```
48//!
49//! It this expression, 'target' is the link to the ActorRef, 'SomeType' is the actor's concrete
50//! type and the 'actor' is the casted variable for inner use.
51//!
52//! Important note! For use this feature, target actor must implement as_any method of the 'Actor'
53//! trait. Without satisfaction this requirement, type cast operation over actor object will be
54//! completed with panic! This is example of simplest implementation of this method:
55//!
56//! ```
57//! fn as_any(self: &Self) -> &Any { self }
58//! ```
59//!
60//! # TestProbe
61//!
62//! This is a heart part of actor's testing. TestProbe is an object, which constructed over inner
63//! actor and allows for external code, interact with it in a blocking manner. Probe may by created
64//! from the test actor system:
65//!
66//! ```
67//! let probe = system.create_probe(Some("my_probe"));
68//! ```
69//!
70//! After that, with him may be performed various operations.
71//!
72//! ### Send
73//!
74//! Sends specified message the specified actor on behalf of the probe actor. Target actor may
75//! respond on this message, and response will be received by the probe actor.
76//!
77//! ### Expectations
78//!
79//! The main feature of the Test Probe consists in than it may expect some messages and fail test
80//! if this messages does not satisfy for specified requirements. For this task, probe presents few
81//! method which names starts with 'expect' prefix. Each of this methods receive as arg one or more
82//! anonymous functions which is called 'matcher'. Matcher receive a message as argument and return
83//! a bool value witch determine result of matching test for a message. All expectors functions is
84//! blocks the called thread. After block, inner actor
85//! must receive a message/messages and pass them through early set matchers. If the inner actor
86//! receive a messages, expector function will be unlocked and decide what to do with results of
87//! matching - pass test or fail it. If the inner actor does not receive any messages in the
88//! specified timeout, expector function also will be unlocked, and test will be failed by timeout
89//! error. Default timeout is set to 3 seconds. This time may be tuned though set_timeout method.
90//! Now let's define list of existed expects.
91//!
92//! * expect_msg - Expects a single message
93//! * expect_msg_any_of - Expects any message from the presented set
94//! * expect_msg_all_off - Expects all message from the presented set. Order of the messages is not
95//! determined. Also target messages may to alternate with message does not from set.
96//! * expect_no_msg - Expects that no one message will be received in the specified time interval
97//!
98//! Now what with matchers. Matcher is a function with next signature:
99//!
100//! ```text
101//! type Matcher = Box<Fn(&Box<Any + Send>) -> bool + Send>;
102//! ```
103//!
104//! Typical operations, which do a matchers, consists in check of message type through it's
105//! downcasting and pass some clarifying checks, such us more precise pattern matching. Example of
106//! typical matcher function is presented below.
107//!
108//!
109//! ```
110//! Box::new(|v: &Box<Any + Send>| {
111//! if let Some(m) = v.downcast_ref::<logger::Log>() {
112//! if m.text.len() > 100 {
113//! match m.target {
114//! logger::LogTarget::StdOut => true,
115//! _ => false
116//! }
117//! } else {
118//! false
119//! }
120//! } else {
121//! false
122//! }
123//!});
124//! ```
125//!
126//! Of course this is a very dirty code and testkit presents few macros, in order to tests code looks
127//! like more clean. First such macro is 'matcher!'. It is the simple wrapper for hiding header
128//! details of the function.
129//!
130//! ```
131//! matcher!(v => {
132//! if let Some(m) = v.downcast_ref::<logger::Log>() {
133//! if m.text.len() > 100 {
134//! match m.target {
135//! logger::LogTarget::StdOut => true,
136//! _ => false
137//! }
138//! } else {
139//! false
140//! }
141//! } else {
142//! false
143//! }
144//! });
145//! ```
146//!
147//! Second type, constructs a function, which checks a message for match to the specified type.
148//!
149//! ```
150//! type_matcher!(logger::Log);
151//! ```
152//!
153//! Third type, constructs a function, which checks a message for match to the specified simple
154//! pattern (without guards).
155//!
156//! ```
157//! pat_matcher!(logger::Log => logger::Log { text: _, target: logger::LogTarget::StdOut });
158//! ```
159//!
160//! Fourth type, is most useful. It constructs a function, which checks a message for match to the
161//! specified type and after that apply to the casted message some user code with additional checks.
162//! This is useful when simple pattern match checks does not sufficient. For example you need call a
163//! method of the object which lives as field of the message.
164//!
165//! ```
166//! extended_type_matcher!(logger::Log, v => {
167//! if v.text.len() > 100 {
168//! true
169//! } else {
170//! false
171//! }
172//! });
173//! ```
174//!
175//! Summarizing the paragraph, below present example of usage expector-functions with described
176//! matchers.
177//!
178//! ```
179//! probe.expect_msg(type_matcher!(logger::Log));
180//!
181//! // or
182//!
183//! probe.expect_msg_any_of(
184//! vec! [
185//! pat_matcher!(logger::Log => logger::Log { text: _, target: logger::LogTarget::StdOut }),
186//! pat_matcher!(logger::Log => logger::Log { text: _, target: logger::LogTarget::File })
187//! ]
188//! );
189//!
190//! ```
191//!
192//! ### Reply
193//!
194//! This function permits to you send a message to the sender of last received message. In other
195//! words, you may respond to the sender. This allow to you construct message passing chains with
196//! target actor. You send message to the target actor, receive response from it and if it is
197//! matched, reply to them with new message, and again wait a message and so on.
198//!
199//! ```
200//! probe.send(target.clone(), Box::new(some_actor::DoFirstAction { }));
201//! probe.expect_msg(type_matcher!(some_actor::OkForFirstAction));
202//! probe.send(target.clone(), Box::new(some_actor::DoSecondAction { }));
203//! probe.expect_msg(type_matcher!(some_actor::OkForSecondAction));
204//!
205//! ```
206//!
207//! ### DI testing
208//!
209//! Internal actor is a regular actor existed under standard ActorRef. This ActorRef may be
210//! extracted, cloned and passed as dependency to the tested actor. This operation is names as
211//! dependency injection testing. Injected test actor reference will be receive all message which
212//! will be addressed to emulated dependency. For see hiw it work, remember example with logger
213//! from the main doc. Logger actor receive few actors as dependency's, which realize concrete
214//! logic of writing logs. Logger after receive the Log message, determines log target and send
215//! command for write log, to the specified writer. All this logic may tested by fully transparent
216//! way:
217//!
218//! ```
219//! let (mut target, mut probe, mut stdout_writer, mut file_writer) = {
220//! let mut system = system.lock().unwrap();
221//! let mut probe = system.create_probe(Some("probe"));
222//! let mut stdout_writer = system.create_probe(Some("stdout_writer"));
223//! let mut file_writer = system.create_probe(Some("file_writer"));
224//! let mut target = system.actor_of(logger::props(file_writer.aref(), stdout_writer.aref()), Some("logger"));
225//!
226//! (target, probe, stdout_writer, file_writer)
227//! };
228//!
229//! probe.send(target.clone(), Box::new(logger::Log { text: String::from("_"), target: logger::LogTarget::File }));
230//! file_writer.expect_msg(type_matcher!(file_writer::Write));
231//!
232//! probe.send(target.clone(), Box::new(logger::Log { text: String::from("_"), target: logger::LogTarget::StdOut }));
233//! stdout_writer.expect_msg(type_matcher!(stdout_writer::Write));
234//! ```
235//!
236//! # Examples
237//!
238//! In the 'examples/testkit/bagsman.rs' presented all types of test actions which you may do with
239//! this testkit. Initially all tests in this file is passable, but in the commented blocks of code,
240//! defined various situations with tests which will be failed for some reasons. Uncomment it fow see
241//! what to happen.
242
243#[macro_use]
244pub mod macrodef;
245pub mod test_local_actor_system;
246pub mod test_local_actor_ref;
247pub mod test_probe;
248
249