整洁的测试方式成为scala中不受欢迎的swtichover

时间:2016-08-20 01:15:29

标签: scala unit-testing tdd akka actor

我正在尝试测试接收方法的状态切换。 找到this stackoverflow帖子,但这也没有明确给出解决方案。 请在下面找到简化的代码段: -

package become_unbecome_basics

import akka.actor.{Actor, ActorSystem}
import akka.testkit.{ImplicitSender, TestActorRef, TestKit}
import become_unbecome_basics.BasicBecomeUnbecomeActor.{SWITCH_TO_MASTER, SWITCH_TO_STANDBY}
import com.typesafe.scalalogging.LazyLogging
import org.scalatest.FlatSpecLike
import org.scalatest.Matchers._

class BecomUnbecomeSwitchoverTest extends TestKit(ActorSystem("testSystem")) with ImplicitSender with FlatSpecLike{

  "initially receive" should "points to master" in {
    val aRef = TestActorRef[BasicBecomeUnbecomeActor]
    val actor = aRef.underlyingActor

    //not sure, weather we have something like this to assert
    //actor.receive should be(actor.master)
  }
}

object BasicBecomeUnbecomeActor{
  case object SWITCH_TO_MASTER
  case object SWITCH_TO_STANDBY
}

class BasicBecomeUnbecomeActor extends Actor with LazyLogging{
  override def receive: Receive = master

  def master: Receive = {
    case SWITCH_TO_STANDBY =>
      context.become(standBy)

    case msg => logger.debug(s"master : $msg received")
  }

  def standBy: Receive = {
    case SWITCH_TO_MASTER =>
      context.unbecome()

    case msg => logger.debug(s"standBy : $msg received")
  }
}

1 个答案:

答案 0 :(得分:0)

您提到的StackOverflow帖子包含两种测试演员的方法。

  1. 将状态更改发送给其他演员。
  2. 不要测试状态变化,而是测试演员的行为。
  3. 在第一个示例中,您可以通过某种方式在每次状态更改时向您的actor发送信息。在Akka中,将状态更改信息作为actor消息发送是实现此目的的自然方式。

    import akka.actor._
    import akka.testkit._
    
    class ExampleActor(notify: ActorRef) extends Actor with ActorLogging {
      import ExampleActor.{Master, StandBy}
    
      def receive: Receive = master
    
      def master: Receive = {
        case StandBy =>
          notify ! StandBy
          context.become(standby)
    
        case msg =>
          log.debug("received msg in master: {}", msg)
      }
    
      def standby: Receive = {
        case Master =>
          notify ! Master
          context.become(master)
    
        case msg =>
          log.debug("received msg in standby: {}", msg)
      }
    }
    
    object ExampleActor {
      def props(notify: ActorRef): Props = Props(new ExampleActor(notify))
    
      case object Master
      case object StandBy
    }
    
    class ExampleActorTest extends TestKit(ActorSystem("testSystem")) with FlatSpecLike {
    
      "ExampleActor" should "move to stand by state" in {
        val probe = TestProbe()
        val actor = system.actorOf(ExampleActor.props(probe.ref))
    
        actor ! ExampleActor.StandBy
    
        probe.expectMsg(ExampleActor.StandBy)
      }
    }
    

    (我还没有运行代码,所以对代码中的任何错误表示歉意)

    在上面的代码中,ExampleActor是一个有状态的actor,它通知给定的actor引用任何状态的变化。请注意,这不允许检查当前状态,而是检查状态转换的日志。此外,可以在状态通知代码中引入错误,因为通知代码是手动添加到actor而不是自动执行的操作。

    我将测试样式更改为asynchronous testing style以获得更真实的测试。

    状态更改通知允许您获取有关actor转换到的具体状态的信息,但它不会告诉您它是否按照应有的方式工作。不是测试演员经历的状态变化,而是如何测试演员本身所做的事情。

    class Accumulator extends Actor with ActorLogging {
    
      import Accumulator._
    
      def receive: Receive = accumulatorReceive(0)
    
      def accumulatorReceive(x: Int): Receive = {
        case Add(i) => next(x + i)
        case Remove(i) => next(x - i)
        case Multiply(i) => next(x * i)
        case Divide(i) => next(x / i)
        case Get => sender() ! x
      }
    
      def next(x: Int) = context.become(accumulatorReceive(x))
    }
    
    object Accumulator {
      def props: Props = Props(new Accumulator)
    
      case class Add(i: Int)
      case class Remove(i: Int)
      case class Multiply(i: Int)
      case class Divide(i: Int)
      case object Get
    }
    
    class AccumulatorTest extends TestKit(ActorSystem("testSystem")) with FlatSpecLike {
    
      import Accumulator._
    
      "Accumulator" should "accumulate" in {
        val probe = TestProbe()
        val actor = system.actorOf(Accumulator.props)
    
        actor ! Add(3)
        actor ! Remove(1)
        actor ! Multiply(4)
        actor ! Divide(2)
    
        probe.send(actor, Get)
        probe.expectMsg(5)
      }
    }
    

    在此示例中,Accumulator执行状态更改,但在状态发生更改时不会通知。相反,它有一个特定的get命令,用于检查有关其状态的有趣部分。在测试中,我们发送多条消息,这些消息会导致累加器actor中的状态发生变化。最后,我们通过查询累加器来检查这些消息的结果。