В первых двух частях ( одна , две ) мы кратко говорили об актерах и о том, как работает обмен сообщениями.  В этой части давайте рассмотрим исправление регистрации и тестирования нашего TeacherActor . 
резюмировать
Вот как выглядел наш актер из предыдущей части:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 | classTeacherActor extendsActor {  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 = {    caseQuoteRequest => {      importutil.Random      //Get a random Quote from the list and construct a response      val quoteResponse=QuoteResponse(quotes(Random.nextInt(quotes.size)))      println (quoteResponse)    }  }} | 
Регистрация Akka с SLF4J
  Вы замечаете, что в коде мы quoteResponse к стандартному выводу, который вы, очевидно, согласитесь, — плохая идея.  Давайте исправим это, включив регистрацию через фасад SLF4J. 
1. Исправьте класс, чтобы использовать ведение журнала
  Akka предлагает ActorLogging черту ActorLogging для достижения этой цели.  Давайте смешаем это в: 
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | classTeacherLogActor extendsActor 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 = {    caseQuoteRequest => {      importutil.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 и Logging
  EventStream ведет себя так же, как брокер сообщений, которому мы можем публиковать и получать сообщения.  Одно тонкое отличие от обычной MOM заключается в том, что подписчиками EventStream может быть только Актер. 
В случае регистрации сообщений все сообщения журнала будут опубликованы в EventStream. По умолчанию подписчик на эти сообщения — это DefaultLogger, который просто печатает сообщение в стандартный вывод.
| 1 2 3 4 5 6 | classDefaultLogger extendsActor with StdOutLogger {      override def receive: Receive = {        ...        caseevent: LogEvent ⇒ print(event)    }} | 
Вот почему, когда мы пытаемся запустить StudentSimulatorApp , мы видим сообщение журнала, записанное на консоль.
Тем не менее, EventStream не подходит только для регистрации. Это универсальный механизм публикации-подписки, доступный внутри ActorWorld внутри виртуальной машины (подробнее об этом позже).
Вернуться к настройке SLF4J:
2. Настройте Akka для использования SLF4J
| 1 2 3 4 5 | akka{      loggers = ["akka.event.slf4j.Slf4jLogger"]    loglevel = "DEBUG"    logging-filter = "akka.event.slf4j.Slf4jLoggingFilter"} | 
  Мы храним эту информацию в файле с именем application.conf который должен находиться в вашем пути к классам.  В нашей структуре папок sbt мы добавили бы это в вашу директорию main/resources . 
Из конфигурации мы можем вывести, что:
-   свойство loggersуказывает Актера, который собирается подписаться на события журнала. Что делает Slf4jLogger , так это просто использует сообщения журнала и передает их фасаду SLF4J Logger.
-   свойство loglevelпросто указывает минимальный уровень, который следует учитывать при ведении журнала.
-   logging-filterсравниваетloglevelнастроенный уровень журнала и уровень входящего сообщения журнала и отбрасывает любое сообщение журнала ниже настроенного уровня журнала перед публикацией в EventStream.
Но почему у нас не было application.conf для предыдущего примера?
  Просто потому, что Akka предоставляет некоторые нормальные значения по умолчанию, так что нам не нужно создавать файл конфигурации, прежде чем мы начнем с ним играть.  Здесь мы будем слишком часто возвращаться к этому файлу для настройки различных вещей.  Существует целый ряд потрясающих параметров, которые вы можете использовать внутри application.conf для самостоятельной регистрации.  Они подробно объяснены здесь . 
3. Добавьте logback.xml
Сейчас мы будем настраивать регистратор SLF4J с поддержкой logback .
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | <?xmlversion="1.0"encoding="UTF-8"?>  <configuration>      <appendername="FILE"        class="ch.qos.logback.core.rolling.RollingFileAppender">        <encoder>            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>        </encoder>        <rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">            <fileNamePattern>logs\akka.%d{yyyy-MM-dd}.%i.log</fileNamePattern>            <timeBasedFileNamingAndTriggeringPolicyclass="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">                <maxFileSize>50MB</maxFileSize>            </timeBasedFileNamingAndTriggeringPolicy>        </rollingPolicy>    </appender>    <rootlevel="DEBUG">        <appender-refref="FILE"/>    </root></configuration> | 
  Я бросил это в папку main/resources вместе с application.conf .  Убедитесь, что main/resources теперь находится в вашем затмении или в другом пути к классам IDE.  Также добавьте logback и slf4j-api в ваш build.sbt . 
  И когда мы TeacherLogActor akkaxxxxx.log и отправляем сообщение нашему новому TeacherLogActor , akkaxxxxx.log файл akkaxxxxx.log выглядит следующим образом. 
Тестирование Акки
Обратите внимание, что это ни в коем случае не является исчерпывающим охватом Testing Akka. Мы построим наши тесты на дополнительных возможностях тестирования в следующих частях под заголовками соответствующих тем. Эти тестовые сценарии нацелены на актеров, которых мы написали ранее.
  В то время как StudentSimulatorApp делает то, что нам нужно, вы согласитесь с тем, что его следует вытеснить из тестовых случаев. 
Чтобы облегчить задачу тестирования, Akka разработала удивительный инструментарий тестирования, с помощью которого мы могли бы выполнять некоторые магические действия, например, исследовать внутреннюю часть реализации Actor.
Хватит разговоров, посмотрим на тестовые случаи.
Давайте сначала попробуем отобразить StudentSimulatorApp в Testcase.
Давайте теперь посмотрим на одну декларацию.
| 1 2 3 4 | classTeacherPreTest extendsTestKit(ActorSystem("UniversityMessageSystem"))    with WordSpecLike  with MustMatchers  with BeforeAndAfterAll { | 
Итак, из определения класса TestCase мы видим, что:
-   Черта TestKitпринимаетActorSystemчерез которую мы будем создавать Actors. Внутри TestKit украшает ActorSystem и заменяет диспетчер по умолчанию.
- Мы используем WordSpec, который является одним из многих забавных способов написания тестовых случаев с ScalaTest.
- MustMatchers предоставляют удобные методы, чтобы сделать тестовый пример похожим на естественный язык
-   Мы BeforeAndAfterAllчтобыBeforeAndAfterAllработу ActorSystem после завершения тестовых случаев. МетодafterAllкоторый обеспечивает эта черта, больше похож на нашtearDownв JUnit
1, 2 — отправка сообщения актерам
- Первый тестовый пример просто отправляет сообщение в PrintActor. Это ничего не утверждает!
- Во втором случае сообщение отправляется субъекту Log, который использует поле журнала ActorLogging для публикации сообщения в EventStream. Это тоже ничего не утверждает!
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | //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 — Утверждение внутреннего состояния актеров
  В третьем случае используется метод TestActorRef и вызывается метод quoteList .  Метод quoteList возвращает список цитат обратно.  Мы используем этот список, чтобы утверждать его размер. 
  Если ссылка на quoteList отбрасывает вас назад, обратитесь к указанному выше коду TeacherLogActor и найдите: 
| 1 2 3 | //From TeacherLogActor//We'll cover the purpose of this method in the Testing section  def quoteList=quotes | 
| 1 2 3 4 5 6 7 | //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 and Logging (выше), все сообщения журнала отправляются на EventStream и SLF4JLogger подписывается на него и использует свои приложения для записи в файл / консоль журнала и т. Д. Не было бы неплохо подписаться на EventStream прямо в нашем тестовом примере и утверждать наличие самого сообщения журнала?  Похоже, мы тоже можем это сделать. 
Это включает в себя два этапа:
-   Вам нужно добавить дополнительную конфигурацию в ваш TestKitнапример, так:1234classTeacherTestextendsTestKit(ActorSystem("UniversityMessageSystem", ConfigFactory.parseString("""akka.loggers = ["akka.testkit.TestEventListener"]""")))with WordSpecLikewith MustMatcherswith BeforeAndAfterAll {
-   Теперь, когда у нас есть подписка на EventStream, мы можем утверждать ее из нашего тестового примера как:
12345678//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 — Актеры тестирования с параметрами конструктора
  Обратите внимание, что способ создания Actors в TestActorRef[TeacherLogActor] — это TestActorRef[TeacherLogActor] а не system.actorOf .  Это просто для того, чтобы мы могли получить доступ к внутренним объектам актера через метод basicActor в TeacherActorRef.  Мы не сможем достичь этого через ActorRef которому у нас есть доступ во время обычной среды выполнения.  (Это не дает нам никаких оправданий для использования TestActorRef в производстве. Вы будете охотиться). 
Если Actor принимает параметры, то способ создания TestActorRef будет таким:
| 1 | val teacherRef = TestActorRef(newTeacherLogParameterActor(quotes)) | 
Весь тестовый пример будет выглядеть примерно так:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | //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(newTeacherLogParameterActor(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 жизненного цикла afterAll : 
| 1 2 3 4 | override def afterAll() {      super.afterAll()    system.shutdown()  } | 
КОД
- Как всегда, весь проект можно скачать с github здесь .
| Ссылка: | Заметки Акки — регистрация актеров и тестирование от нашего партнера JCG Аруна Маниваннана в блоге Rerun.me . | 

