As a unit test, an Akka accer who sends a message to himself without using Thread.sleep

I have a Scala unit test for Akka Akka. The actor is designed to poll the remote system and update the local cache. Part of the actorโ€™s design is that he doesnโ€™t try to poll while he is still processing or awaiting the result of the last poll in order to avoid flooding the remote system when he is experiencing slowdown.

I have a test case (shown below) that uses Mockito to simulate a slow network call and checks that when the actor is informed about the update, he will not make another network call until the current one is completed. He verifies that the actor did not make another call, checking for lack of interaction with the remote service.

I want to remove the call to Thread.sleep . I want to test the actorโ€™s functionality, not relying on the expectation of hard time, in every test run, which is fragile, and wastes time. The test can interrogate or block, waiting for the condition, with a timeout. This will be more reliable and will not lose time when the test passes. I also have an added restriction that I want to maintain the state used to prevent the var allowPoll from limiting the number of polls PollingActor .

  • Is there a way to keep waiting until the actor completes the messaging itself? If there is a way, I can wait until this before trying to argue.
  • Do I need to send an internal message? Can I maintain internal state with a streaming data structure like java.util.concurrent.AtomicBoolean . I did this, and this code seems to work, but I'm not knowledgeable enough about Akka to see if she was discouraged - a colleague recommended a self-help style.
  • Are there more efficient features with the same semantics? Then I would choose the integration test instead of unit test, although I'm not sure if it will solve this problem.

The current actor looks something like this:

 class PollingActor(val remoteService: RemoteServiceThingy) extends ActWhenActiveActor { private var allowPoll: Boolean = true def receive = { case PreventFurtherPolling => { allowPoll = false } case AllowFurtherPolling => { allowPoll = true } case UpdateLocalCache => { if (allowPoll) { self ! PreventFurtherPolling remoteService.makeNetworkCall.onComplete { result => { self ! AllowFurtherPolling // process result } } } } } } trait RemoteServiceThingy { def makeNetworkCall: Future[String] } private case object PreventFurtherPolling private case object AllowFurtherPolling case object UpdateLocalCache 

And the unit test, in specs2, looks like this:

 "when request has finished a new requests can be made" ! { val remoteService = mock[RemoteServiceThingy] val actor = TestActorRef(new PollingActor(remoteService)) val slowRequest = new DefaultPromise[String]() remoteService.makeNetworkCall returns slowRequest actor.receive(UpdateLocalCache) actor.receive(UpdateLocalCache) slowRequest.complete(Left(new Exception)) // Although the test calls the actor synchronously, the actor calls *itself* asynchronously, so we must wait. Thread.sleep(1000) actor.receive(UpdateLocalCache) there was two(remoteService).makeNetworkCall } 
+6
source share
1 answer

The way we decided to solve this at the moment is to insert the equivalent of an observer into the actor (associated with an existing registrar that was not included in the listing in the question). Then the actor can tell the observer when he moved from different states. In the test code, we perform an action and then wait for the appropriate notification from the actor before continuing and making statements.

In the test, we have something like this:

 actor.receive(UpdateLocalCache) observer.doActionThenWaitForEvent( { actor.receive(UpdateLocalCache) }, // run this action "IgnoredUpdateLocalCache" // then wait for the actor to emit an event } // assert on number of calls to remote service 

I don't know if there is a more idiomatic way, this seems like a reasonable suggestion to me.

+4
source

Source: https://habr.com/ru/post/952227/


All Articles