[翻譯]AKKA筆記 - LOGGING與測試ACTORS -2 (二)

3.THROW IN A LOGBACK.XML

如今咱們把SLF4J日誌配置在logbackhtml

<?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文件裏。git

當咱們啓動StudentSimulatorApp併發了一條消息給咱們的新TeacherLogActor,咱們配置的輸出日誌文件akkaxxxxx.log文件是這樣的。github

圖片描述

測試AKKA

咱們這裏並沒有意進行一個詳盡的Akka覆蓋測試。咱們會在下面增長新特性的時候進行測試。這些測試用例主要是用來覆蓋咱們以前寫的Actors代碼。api


StudentSimulatorApp作了咱們想要的,微信

想擺脫測試之痛, Akka帶了一套很牛的測試工具能讓咱們作一些很神奇的事情,例如讓你的測試代碼直接進入到Actor的內部實現裏。併發

說的差很少了,讓咱們看下測試用例。app

讓咱們先將StudentSimulatorApp映射到一個測試用例(Testcase)上。eclipse

圖片描述

讓咱們看一下代碼的聲明:ide

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」。轉載請註明。
圖片描述

相關文章
相關標籤/搜索