[翻译]AKKA笔记 - LOGGING与测试ACTORS -2 (二)

生成对抗
• 阅读 2138

3.THROW IN A LOGBACK.XML

现在我们把SLF4J日志配置在logback

<?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的classpath中。并且把logback和slf4j-api放到你的build.sbt文件里。

当我们启动StudentSimulatorApp并发了一条消息给我们的新TeacherLogActor,我们配置的输出日志文件akkaxxxxx.log文件是这样的。

[翻译]AKKA笔记 - LOGGING与测试ACTORS -2 (二)

测试AKKA

我们这里并无意进行一个详尽的Akka覆盖测试。我们会在下面增加新特性的时候进行测试。这些测试用例主要是用来覆盖我们之前写的Actors代码。


StudentSimulatorApp做了我们想要的,

想摆脱测试之痛, Akka带了一套很牛的测试工具能让我们做一些很神奇的事情,例如让你的测试代码直接进入到Actor的内部实现里。

说的差不多了,让我们看下测试用例。

让我们先将StudentSimulatorApp映射到一个测试用例(Testcase)上。

[翻译]AKKA笔记 - LOGGING与测试ACTORS -2 (二)

让我们看一下代码的声明:

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

所以,从TestCase的用例定义我们可以看到:

1.TestKitActorSystem接受一个我们要创建的Actors.在内部,TestKit装饰了ActorSystem并且替换了缺省的分发者(dispatcher)。
2.我们在写ScalaTest的测试用例时会使用WordSpec,它可以用许多有趣的方式驱邪。
3.MustMatchers提供便利的方法让测试写起来像自然语言。
4.我们把BeforeAndAfterAll加进来是因为它可以在测试用例结束后关掉ActorSystem。afterAll方法提供的特性很像JUnit中的tearDown方法。

1,2 - 发送消息给ACTORS

1)在第一个测试用例时我们发送了一个消息给PrintActor。但并没有断言什么东西 :-(

2)在第二个例子中我们发了一个消息给日志actor,它用一个ActorLogging发送消息给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-断言(ASSERTING)ACTORS的内部状态

第三个例子用TestActorRefunderlyingActor方法去调用TeacherActorquoteListquoteList方法返回格言(quoteList)的列表。我们用这个列表来断言它的size。

如果说到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,所有的log消息都会发送到EventStream然后SLF4JLogger订阅了这些消息并将其写到日志文件或控制台等。如果让我们的测试用例订阅EventStream并直接断言log消息不是更妙?看起来值得一试。

这需要两步:

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这块代码拦截了一条以QuoteResponse(pattern='QuoteResponse* )开头的消息。(如果用start=‘QuoteResponse'也一样能拦截到)。日过没有一条日志消息发送给TeacherLogActor,这个测试用例就会失败。

5 - 用构造函数参数测试ACTORS

请注意我们在用例中创建Actors时是用TestActorRef[TeacherLogActor]而不是用system.actorOf。这是因为我们可以通过TeacherActorRef的underlyingActor方法来进入Actor的内部。我们用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()
  }

CODE 代码

跟往常一样,整个项目可以在github这里下载。


文章来自微信平台「麦芽面包」,微信号「darkjune_think」。转载请注明。
[翻译]AKKA笔记 - LOGGING与测试ACTORS -2 (二)

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
Java日志体系(八)最佳实践
java常用日志框架关系Log4j2与Log4j1发生了很大的变化,Log4j2不兼容Log4j1。Logback必须配合Slf4j使用。由于Logback和Slf4j是同一个作者,其兼容性不言而喻。比较常用的组合使用方式是Slf4j与Logback组合使用,Commons
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
3年前
LogBack入门实践
一、简介LogBack是一个日志框架,它是Log4j作者Ceki的又一个日志组件。LogBack,Slf4j,Log4j之间的关系  slf4j是TheSimpleLoggingFacadeforJava的简称,是一个简单日志门面抽象框架,它本身只提供了日志FacadeAPI和一个简单的日志类实现,一般常配合Lo
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Cisco配置发送日志到日志服务器
Cisco配置发送日志到日志服务器logging133.3.3.2loggingonloggingtrap7 //指定日志消息的级别(0:紧急(Emergencies)1:告警(Alerts)2:严重的(Critical)3:错误(Errors)4:警告(Warnings)5:通知(Notifications)6:信
Stella981 Stella981
3年前
Akka系列之Logging
在akka中如何使用logakka中显示日志不需要第三方jar,直接使用akka的Logging即可,Logging默认是输出到STDOUT,当然你也可以使用第三方插件如SLF4J来记录日志,Logging使用的异步方式以此来达到最小的性能损耗.代码中使用Loggingimportakka.actor.;
Stella981 Stella981
3年前
Python3常用模块
logging模块一、日志级别CRITICAL50FATALCRITICALERROR40WARNING30WARNWARNINGINFO20DEBUG10NOTSET0不设置二、默认级
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(