Testing

Testing concurrent systems can be more difficult than single threaded applications, since the test itself and the application are running on separate threads. Moreover, because of Riker's resilient self-healing approach where panics are isolated, handled and the failed component restarted, detecting failures in tests proves challenging.

To help make testing easier, the riker-testkit introduces a 'probe' that can be sent to actors either through messaging or as part of an actor's Props. Probes can then emit values back to the test thread.

Here's an example of testing an actor restart:

  1. #[macro_use]
  2. extern crate riker_testkit;
  3.  
  4. type TestProbe = ChannelProbe<(), ()>;
  5.  
  6. #[derive(Clone, Debug)]
  7. enum TestMsg {
  8. Probe(TestProbe),
  9. Panic,
  10. }
  11.  
  12. impl Into<ActorMsg<TestMsg>> for TestMsg {
  13. fn into(self) -> ActorMsg<TestMsg> {
  14. ActorMsg::User(self)
  15. }
  16. }
  17.  
  18. struct MyActor;
  19.  
  20. impl MyActor {
  21. fn new() -> BoxActor<TestMsg> {
  22. Box::new(MyActor)
  23. }
  24. }
  25.  
  26. impl Actor for MyActor {
  27. type Msg = TestMsg;
  28.  
  29. fn receive(&mut self,
  30. _ctx: &Context<Self::Msg>,
  31. msg: Self::Msg,
  32. _sender: Option<ActorRef<Self::Msg>>)
  33. {
  34. match msg {
  35. TestMsg::Panic => {
  36. // panic the actor to simulate failure
  37. panic!("// TEST PANIC // TEST PANIC // TEST PANIC //");
  38. }
  39. TestMsg::Probe(probe) => {
  40. // received probe
  41. // let's emit () empty tuple back to listener
  42. probe.event(());
  43. }
  44. };
  45. }
  46. }
  47.  
  48. #[test]
  49. fn panic_actor() {
  50. let model: DefaultModel<TestMsg> = DefaultModel::new();
  51. let sys = ActorSystem::new(&model).unwrap();
  52.  
  53. let props = Props::new(Box::new(MyActor::new));
  54. let actor = sys.actor_of(props, "my-actor").unwrap();
  55.  
  56. // Make the test actor panic
  57. actor.tell(TestMsg::Panic, None);
  58.  
  59. // Prepare a probe
  60. let (probe, listen) = probe::<()>();
  61.  
  62. // Send the probe to the panicked actor
  63. // which would have been restarted
  64. actor.tell(TestMsg::Probe(probe), None);
  65.  
  66. // Testkit provides a macro to assert the result
  67. // that gets emmitted from the probe to the listener.
  68. // Here we're expecting () empty tuple.
  69. p_assert_eq!(listen, ());
  70. }

This test shows that our actor successfully restarts after it fails. It is able to continue receiving messages, in this case the probe. The macro p_assert_eq! waits (blocks) on the listener until a value is received from the probe.