文章出處

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

我們這里并無意進行一個詳盡的Akka覆蓋測試。我們會在下面增加新特性的時候進行測試。這些測試用例主要是用來覆蓋我們之前寫的Actors代碼。


StudentSimulatorApp做了我們想要的,

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

說的差不多了,讓我們看下測試用例。

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

讓我們看一下代碼的聲明:

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」。轉載請注明。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()