object testkit is not a member of package akka

782 views Asked by At

I am trying to run the example code of Akka FSM but I've met some error

[info] Loading project definition from /home/akka/fsm/project

[info] Loading settings for project root from build.sbt ...
[info] Set current project to fsm (in build file:/home/akka/fsm/)
[info] Executing in batch mode. For better performance use sbt's shell
[info] Compiling 1 Scala source to /home/akka/fsm/target/scala-2.13/classes ...
[error] /home/akka/fsm/src/main/scala/FSMDocSpec.scala:9:13: object testkit is not a member of package akka
[error] import akka.testkit.{ AkkaSpec => MyFavoriteTestFrameWorkPlusAkkaTestKit }
[error]             ^
[error] /home/akka/fsm/src/main/scala/FSMDocSpec.scala:11:13: object testkit is not a member of package akka
[error] import akka.testkit._
[error]             ^
[error] /home/akka/fsm/src/main/scala/FSMDocSpec.scala:43:26: not found: type MyFavoriteTestFrameWorkPlusAkkaTestKit
[error] class FSMDocSpec extends MyFavoriteTestFrameWorkPlusAkkaTestKit {
[error]                          ^
[error] /home/akka/fsm/src/main/scala/FSMDocSpec.scala:203:33: value must is not a member of String
[error]   "simple finite state machine" must {
[error]                                 ^
[error] /home/akka/fsm/src/main/scala/FSMDocSpec.scala:205:32: value in is not a member of String
[error]     "demonstrate NullFunction" in {
[error]                                ^
[error] /home/akka/fsm/src/main/scala/FSMDocSpec.scala:214:23: value in is not a member of String
[error]     "batch correctly" in {
[error]                       ^
[error] /home/akka/fsm/src/main/scala/FSMDocSpec.scala:215:21: not found: value system
[error]       val buncher = system.actorOf(Props(classOf[Buncher], this))
[error]                     ^
[error] /home/akka/fsm/src/main/scala/FSMDocSpec.scala:219:7: not found: value expectMsg
[error]       expectMsg(Batch(immutable.Seq(42, 43)))
[error]       ^
[error] /home/akka/fsm/src/main/scala/FSMDocSpec.scala:223:7: not found: value expectMsg
[error]       expectMsg(Batch(immutable.Seq(44)))
[error]       ^
[error] /home/akka/fsm/src/main/scala/FSMDocSpec.scala:224:7: not found: value expectMsg
[error]       expectMsg(Batch(immutable.Seq(45)))
[error]       ^
[error] /home/akka/fsm/src/main/scala/FSMDocSpec.scala:227:34: value in is not a member of String
[error]     "not batch if uninitialized" in {
[error]                                  ^
[error] /home/akka/fsm/src/main/scala/FSMDocSpec.scala:228:21: not found: value system
[error]       val buncher = system.actorOf(Props(classOf[Buncher], this))
[error]                     ^
[error] /home/akka/fsm/src/main/scala/FSMDocSpec.scala:230:7: not found: value expectNoMsg
[error]       expectNoMsg
[error]       ^
[error] 13 errors found

My code is like:

import language.postfixOps

import akka.testkit.{ AkkaSpec => MyFavoriteTestFrameWorkPlusAkkaTestKit }
import akka.util.ByteString
import akka.testkit._

//#test-code
import akka.actor.Props
import scala.collection.immutable

object FSMDocSpec {
  // messages and data types
  //#test-code
  import akka.actor.ActorRef
  //#simple-events
  // received events
  final case class SetTarget(ref: ActorRef)
  final case class Queue(obj: Any)
  case object Flush

  // sent events
  final case class Batch(obj: immutable.Seq[Any])
  //#simple-events
  //#simple-state
  // states
  sealed trait State
  case object Idle extends State
  case object Active extends State

  sealed trait Data
  case object Uninitialized extends Data
  final case class Todo(target: ActorRef, queue: immutable.Seq[Any]) extends Data
  //#simple-state
  //#test-code
}

class FSMDocSpec extends MyFavoriteTestFrameWorkPlusAkkaTestKit {
  import FSMDocSpec._

  //#fsm-code-elided
  //#simple-imports
  import akka.actor.{ ActorRef, FSM }
  import scala.concurrent.duration._
  //#simple-imports
  //#simple-fsm
  class Buncher extends FSM[State, Data] {

    //#fsm-body
    startWith(Idle, Uninitialized)

    //#when-syntax
    when(Idle) {
      case Event(SetTarget(ref), Uninitialized) =>
        stay.using(Todo(ref, Vector.empty))
    }
    //#when-syntax

    //#transition-elided
    onTransition {
      case Active -> Idle =>
        stateData match {
          case Todo(ref, queue) => ref ! Batch(queue)
          case _                => // nothing to do
        }
    }
    //#transition-elided
    //#when-syntax

    when(Active, stateTimeout = 1 second) {
      case Event(Flush | StateTimeout, t: Todo) =>
        goto(Idle).using(t.copy(queue = Vector.empty))
    }
    //#when-syntax

    //#unhandled-elided
    whenUnhandled {
      // common code for both states
      case Event(Queue(obj), t @ Todo(_, v)) =>
        goto(Active).using(t.copy(queue = v :+ obj))

      case Event(e, s) =>
        log.warning("received unhandled request {} in state {}/{}", e, stateName, s)
        stay
    }
    //#unhandled-elided
    //#fsm-body

    initialize()
  }
  //#simple-fsm
  object DemoCode {
    trait StateType
    case object SomeState extends StateType
    case object Processing extends StateType
    case object Error extends StateType
    case object Idle extends StateType
    case object Active extends StateType

    class Dummy extends FSM[StateType, Int] {
      class X
      val newData = 42
      object WillDo
      object Tick

      //#modifier-syntax
      when(SomeState) {
        case Event(msg, _) =>
          goto(Processing).using(newData).forMax(5 seconds).replying(WillDo)
      }
      //#modifier-syntax

      //#transition-syntax
      onTransition {
        case Idle -> Active => startTimerWithFixedDelay("timeout", Tick, 1 second)
        case Active -> _    => cancelTimer("timeout")
        case x -> Idle      => log.info("entering Idle from " + x)
      }
      //#transition-syntax

      //#alt-transition-syntax
      onTransition(handler _)

      def handler(from: StateType, to: StateType): Unit = {
        // handle it here ...
      }
      //#alt-transition-syntax

      //#stop-syntax
      when(Error) {
        case Event("stop", _) =>
          // do cleanup ...
          stop()
      }
      //#stop-syntax

      //#transform-syntax
      when(SomeState)(transform {
        case Event(bytes: ByteString, read) => stay.using(read + bytes.length)
      }.using {
        case s @ FSM.State(state, read, timeout, stopReason, replies) if read > 1000 =>
          goto(Processing)
      })
      //#transform-syntax

      //#alt-transform-syntax
      val processingTrigger: PartialFunction[State, State] = {
        case s @ FSM.State(state, read, timeout, stopReason, replies) if read > 1000 =>
          goto(Processing)
      }

      when(SomeState)(transform {
        case Event(bytes: ByteString, read) => stay.using(read + bytes.length)
      }.using(processingTrigger))
      //#alt-transform-syntax

      //#termination-syntax
      onTermination {
        case StopEvent(FSM.Normal, state, data)         => // ...
        case StopEvent(FSM.Shutdown, state, data)       => // ...
        case StopEvent(FSM.Failure(cause), state, data) => // ...
      }
      //#termination-syntax

      //#unhandled-syntax
      whenUnhandled {
        case Event(x: X, data) =>
          log.info("Received unhandled event: " + x)
          stay
        case Event(msg, _) =>
          log.warning("Received unknown event: " + msg)
          goto(Error)
      }
      //#unhandled-syntax

    }

    //#logging-fsm
    import akka.actor.LoggingFSM
    class MyFSM extends LoggingFSM[StateType, Data] {
      //#body-elided
      override def logDepth = 12
      onTermination {
        case StopEvent(FSM.Failure(_), state, data) =>
          val lastEvents = getLog.mkString("\n\t")
          log.warning(
            "Failure in state " + state + " with data " + data + "\n" +
            "Events leading up to this point:\n\t" + lastEvents)
      }
      // ...
      //#body-elided
    }
    //#logging-fsm

  }
  //#fsm-code-elided

  "simple finite state machine" must {

    "demonstrate NullFunction" in {
      class A extends FSM[Int, Null] {
        val SomeState = 0
        //#NullFunction
        when(SomeState)(FSM.NullFunction)
        //#NullFunction
      }
    }

    "batch correctly" in {
      val buncher = system.actorOf(Props(classOf[Buncher], this))
      buncher ! SetTarget(testActor)
      buncher ! Queue(42)
      buncher ! Queue(43)
      expectMsg(Batch(immutable.Seq(42, 43)))
      buncher ! Queue(44)
      buncher ! Flush
      buncher ! Queue(45)
      expectMsg(Batch(immutable.Seq(44)))
      expectMsg(Batch(immutable.Seq(45)))
    }

    "not batch if uninitialized" in {
      val buncher = system.actorOf(Props(classOf[Buncher], this))
      buncher ! Queue(42)
      expectNoMsg
    }
  }
}
//#test-code

build.sbt is like:

lazy val root = (project in file(".")).settings (
  name := "fsm",
  version := "1.0",
  scalaVersion := "2.13.1",
  scalacOptions in ThisBuild ++= Seq("-unchecked", "-deprecation"),
  resolvers += "Typesafe Repository" at "https://repo.typesafe.com/typesafe/releases/",
  //libraryDependencies +=  "org.scalatest" %% "scalatest" % "3.0.1" % Test,
  //libraryDependencies += "org.scalactic" %% "scalactic" % "3.0.0",
  libraryDependencies +=  "org.scalatest" %% "scalatest" % "3.1.2" % "test",
  libraryDependencies += "com.typesafe.akka" %% "akka-testkit" % "2.6.5" % Test,
  libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.6.5"
)

I am following the guidance at: https://doc.akka.io/docs/akka/current/fsm.html but I can't get this code run.

I suspect that I do wrong in the build.sbt file but I've got nothing through a whole day searching and trying. Same error is looming around and I wonder what's wrong here.

1

There are 1 answers

0
Tim Moore On BEST ANSWER

You should move FSMDocSpec.scala to the src/test/scala directory, instead of src/main/scala. This will put test-scoped dependencies on the classpath while compiling that file.

In your build.sbt, the scalatest and akka-testkit dependencies are specified to be scoped to the test configuration:

libraryDependencies +=  "org.scalatest" %% "scalatest" % "3.1.2" % "test",
libraryDependencies += "com.typesafe.akka" %% "akka-testkit" % "2.6.5" % Test,

This is the correct way to specify dependencies that are only used when running tests, and not at runtime for the main application, so it's not an error in your sbt configuration. These dependencies will only be available when running in the test configuration, which is used when compiling and running tests. They won't be on the classpath when compiling and running the main application, which allows you to exclude unnecessary libraries from your production application. The string "test" and constant Test are equivalent, so you could choose either one and use it for both dependencies to be consistent, but it's not important to do so.

The test source code itself must be inside src/test/scala. The src/main/scala directory should only be used for the main application code, and will be compiled without the test dependencies on the classpath.

There are more details in the sbt documentation, for example in "Scoping by the configuration axis".