Akka Notes –演员记录和测试

在前两部分( )中,我们简要讨论了Actor以及消息传递的工作方式。 在这一部分中,让我们看一下如何修复并记录我们的TeacherActor

概括

这就是我们上一部分中的Actor的样子:

class TeacherActor extends Actor {

  val quotes = List(
    "Moderation is for cowards",
    "Anything worth doing is worth overdoing",
    "The trouble is you think you have time",
    "You never gonna know if you never even try")

  def receive = {

    case QuoteRequest => {

      import util.Random

      //Get a random Quote from the list and construct a response
      val quoteResponse=QuoteResponse(quotes(Random.nextInt(quotes.size)))

      println (quoteResponse)

    }
  }
}

使用SLF4J记录Akka

您会注意到,在代码中,我们正在将quoteResponse打印到标准输出,您显然会认为这是一个坏主意。 让我们通过启用SLF4J Facade日志进行修复。

1.修复类以使用日志记录

Akka提供了一个很好的小特征ActorLogging来实现它。 让我们混合:

class TeacherLogActor extends Actor with ActorLogging {

   val quotes = List(
    "Moderation is for cowards",
    "Anything worth doing is worth overdoing",
    "The trouble is you think you have time",
    "You never gonna know if you never even try")

  def receive = {

    case QuoteRequest => {

      import util.Random

      //get a random element (for now)
      val quoteResponse=QuoteResponse(quotes(Random.nextInt(quotes.size)))
      log.info(quoteResponse.toString())
    }
  }

  //We'll cover the purpose of this method in the Testing section
  def quoteList=quotes

}

绕道走一圈:

在内部,当我们记录一条消息时,ActorLogging中的记录方法(最终)将日志消息发布到EventStream 。 是的,我确实说过publish 。 那么,EventStream实际上是什么?

EventStream和记录

EventStream行为就像我们可以向其发布和接收消息的消息代理一样。 与常规MOM的一个细微区别是EventStream的订阅者只能是Actor。

在记录消息的情况下,所有日志消息都将发布到EventStream。 默认情况下,订阅这些消息的Actor是DefaultLogger ,它仅将消息打印到标准输出中。

class DefaultLogger extends Actor with StdOutLogger {  
    override def receive: Receive = {
        ...
        case event: LogEvent ⇒ print(event)
    }
}

因此,这就是我们尝试启动StudentSimulatorApp的原因,我们看到了写入控制台的日志消息。

也就是说,Eve​​ntStream不仅适合于记录。 它是VM内ActorWorld内部可用的通用发布-订阅机制(稍后会详细介绍)。

返回SLF4J设置:

2.将Akka配置为使用SLF4J

akka{  
    loggers = ["akka.event.slf4j.Slf4jLogger"]
    loglevel = "DEBUG"
    logging-filter = "akka.event.slf4j.Slf4jLoggingFilter"
}

我们将此信息存储在一个名为application.conf的文件中,该文件应该在您的类路径中。 在我们的sbt文件夹结构中,我们会将其放在您的main/resources目录中。

从配置中,我们可以得出:

  1. loggers属性指示将要订阅日志事件的Actor。 Slf4jLogger所做的只是简单地使用日志消息,并将其委托给SLF4J Logger外观。
  2. loglevel属性仅指示记录日志时应考虑的最低级别。
  3. logging-filter比较当前配置的日志loglevel和传入的日志消息级别,并在发布到EventStream之前剔除配置的日志级别以下的任何日志消息。

但是,为什么前面的示例没有提供application.conf?

仅仅是因为Akka提供了一些合理的默认值,所以我们在开始使用它之前不需要构建配置文件。 为了自定义各种内容,我们将在这里经常重新访问该文件。 您可以在application.conf内部使用一堆很棒的参数来单独记录日志。 它们在这里详细解释。

3.放入logback.xml

现在,我们将配置一个由logback支持的SLF4J记录器。

<?xml version="1.0" encoding="UTF-8"?>  
<configuration>  
    <appender name="FILE"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>

        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs\akka.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>50MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
    </appender>

    <root level="DEBUG">
        <appender-ref ref="FILE" />
    </root>
</configuration>

我也将它与application.conf一起放入了main/resources文件夹中。 请确保main/resources现在位于Eclipse或其他IDE的类路径中。 还应将logback和slf4j-api包含到build.sbt中

而当我们揭开序幕我们StudentSimulatorApp和发送邮件到我们的新TeacherLogActor ,该akkaxxxxx.log文件,我们配置看起来像这样。

SLF4J记录

测试Akka

请注意,这绝不是Test Akka的详尽介绍。 我们将在以下各部分中相应标题下的“测试”的更多功能上构建测试。 这些测试用例旨在覆盖我们之前编写的Actor。

StudentSimulatorApp我们的需求时,您将同意应将其从测试用例中删除。

为了减轻测试的痛苦,Akka提出了一个了不起的测试工具包,使用它我们可以做一些神奇的事情,例如直接探查Actor实现的内部。

聊够了,让我们看看测试用例。

首先让我们尝试将StudentSimulatorApp映射到测试用例。

映射001

现在让我们单独看一下声明。

class TeacherPreTest extends TestKit(ActorSystem("UniversityMessageSystem"))  
  with WordSpecLike
  with MustMatchers
  with BeforeAndAfterAll {

因此,从TestCase类的定义中,我们看到:

  1. TestKit特性接受一个ActorSystem通过它我们可以创建Actor。 在内部,TestKit装饰ActorSystem并替换默认的调度程序。
  2. 我们使用WordSpec ,这是使用ScalaTest编写测试用例的许多有趣方式之一。
  3. MustMatchers提供方便的方法来使测试用例看起来像自然语言
  4. 在测试用例完成之后,我们混入BeforeAndAfterAll以关闭ActorSystem。 特质提供的afterAll方法更像我们在JUnit中的tearDown

1、2 –向演员发送消息

  1. 第一个测试用例只是向PrintActor发送一条消息。 它没有断言!
  2. 第二种情况将消息发送到Log actor,后者使用ActorLogging的log字段将消息发布到EventStream。 这也没有断言!
//1. Sends message to the Print Actor. Not even a testcase actually
  "A teacher" must {

    "print a quote when a QuoteRequest message is sent" in {

      val teacherRef = TestActorRef[TeacherActor]
      teacherRef ! QuoteRequest
    }
  }

  //2. Sends message to the Log Actor. Again, not a testcase per se
  "A teacher with ActorLogging" must {

    "log a quote when a QuoteRequest message is sent" in {

      val teacherRef = TestActorRef[TeacherLogActor]
      teacherRef ! QuoteRequest
    }

3 –维护参与者的内部状态

第三种情况使用TestActorRefunderlyingActor quoteList方法,并调用TeacherActorquoteList方法。 quoteList方法返回引号列表。 我们使用此列表来声明其大小。

如果对quoteList引用使您退缩,请参考上面列出的TeacherLogActor代码,并查找:

//From TeacherLogActor
//We'll cover the purpose of this method in the Testing section
  def quoteList=quotes
//3. Asserts the internal State of the Log Actor. 
    "have a quote list of size 4" in {

      val teacherRef = TestActorRef[TeacherLogActor]
      teacherRef.underlyingActor.quoteList must have size (4)
      teacherRef.underlyingActor.quoteList must have size (4)
    }

4 –声明日志消息

正如我们前面在EventStream和Logging部分(上文)中所讨论的,所有日志消息都发送到EventStream ,并且SLF4JLogger订阅并使用其附加程序将其写入日志文件/控制台等。直接在我们的测试用例中使用EventStream并断言日志消息本身的存在? 看起来我们也可以做到。

这涉及两个步骤:

  1. 您需要像这样向您的TestKit添加额外的配置:
    class TeacherTest extends TestKit(ActorSystem("UniversityMessageSystem", ConfigFactory.parseString("""akka.loggers = ["akka.testkit.TestEventListener"]""")))  
      with WordSpecLike
      with MustMatchers
      with BeforeAndAfterAll {
  2. 现在我们已经订阅了EventStream,我们可以从测试用例中将其声明为:
    //4. Verifying log messages from eventStream
        "be verifiable via EventFilter in response to a QuoteRequest that is sent" in {
    
          val teacherRef = TestActorRef[TeacherLogActor]
          EventFilter.info(pattern = "QuoteResponse*", occurrences = 1) intercept {
            teacherRef ! QuoteRequest
          }
        }

EventFilter.info块仅拦截1条以QuoteResponse( pattern='QuoteResponse* )开头的日志消息。 (您也可以通过使用start='QuoteResponse' 。如果没有消息发送给TeacherLogActor,则该测试用例将失败。

5 –使用构造函数参数测试Actor

请注意,我们在测试用例中创建Actor的方式是通过TestActorRef[TeacherLogActor]而不是通过system.actorOf 。 这只是为了使我们可以通过TeacherActorRef中的underlyingActor Actor方法访问Actor的内部。 我们将无法通过在常规运行时可以访问的ActorRef来实现此ActorRef 。 (这并没有给我们在生产中使用TestActorRef的任何借口。您会被追捕的)。

如果Actor接受参数,那么我们创建TestActorRef的方式将是:

val teacherRef = TestActorRef(new TeacherLogParameterActor(quotes))

整个测试用例将如下所示:

//5. have a quote list of the same size as the input parameter
    " have a quote list of the same size as the input parameter" in {

      val quotes = List(
        "Moderation is for cowards",
        "Anything worth doing is worth overdoing",
        "The trouble is you think you have time",
        "You never gonna know if you never even try")

      val teacherRef = TestActorRef(new TeacherLogParameterActor(quotes))
      //val teacherRef = TestActorRef(Props(new TeacherLogParameterActor(quotes)))

      teacherRef.underlyingActor.quoteList must have size (4)
      EventFilter.info(pattern = "QuoteResponse*", occurrences = 1) intercept {
        teacherRef ! QuoteRequest
      }
    }

关闭ActorSystem

最后, afterAll生命周期方法:

override def afterAll() {  
    super.afterAll()
    system.shutdown()
  }

  • 与往常一样,可以从github这里下载整个项目。

翻译自: https://www.javacodegeeks.com/2014/10/akka-notes-actor-logging-and-testing.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值