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.TestKit從ActorSystem接受一個我們要創建的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的內部狀態
第三個例子用TestActorRef的underlyingActor方法去調用TeacherActor的quoteList。quoteList方法返回格言(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」。轉載請注明。
文章列表