2016-12-13 6 views
1

Я актер, который спрашивает другой актер и будущие может откаты другим:Как проверить задать шаблон с запасным вариантом

class Subject(db: ActorRef) extends Actor with ActorLogging { 

    implicit val timeout = Timeout(1 seconds) 
    implicit val ec = context.system.dispatcher 

    override def preStart(): Unit = { 
    db ? "auth" fallbackTo Future { 
     "auth:timeout_error" 
    } pipeTo self 
    } 

    def receive: Receive = { 
    case msg: String => log.info(msg) 
    } 

} 

мне нужно проверить резервное поведение, но не знаю, как это сделать:

class ActorSpec extends TestKit(ActorSystem("MySpec")) 
    with ImplicitSender with WordSpecLike with BeforeAndAfterAll with BeforeAndAfterEach with Matchers { 

    val db = TestProbe() 

    db.ref ! PoisonPill 
    //db not exist anymore 
    val subject = system.actorOf(Props(new Subject(db.ref))) 

    //Something like: subject should receive "auth:timeout_error" 

} 

Как выполнить эту задачу должным образом?

+0

Возможно, что вы хотите иметь, это своего рода автомат? То есть. субъект сначала в состоянии 'initializing', и как только он получает ответ от актера' db', он либо находится в 'fail', либо' running'? См. 'Context.become'. – rethab

+0

@rethab Да, костюмы для государственных машин для этого. Но как я могу проверить, что актер становится «неудачным»? Мой вопрос более общий о практике тестирования. Типичный актерский тест выглядит следующим образом: «отправить сообщение субъекту, проверить его состояние, проверить правильный ответ». Но по моему сценарию мне нужно проверить, что субъект получает (!) Правильное сообщение. – zella

+0

То, что я обычно делаю: либо я добавляю явные сообщения запроса, где я мог бы задать вопрос, находится ли он в состоянии 'running', или я бы послал сообщение теме, которую, как я знаю, обрабатывает, только если она находится в состоянии' running' , Если я получаю ответ, он запускается в состоянии. – rethab

ответ

1

Самый простой способ выполнить тестирование было бы реорганизовать Subject класс, чтобы добавить уровень абстракции к параметру db. В Subject ничего нет, что неотъемлемо зависит от того, что db является ActorRef; Subject просто нужно что-то отправить запрос String и получить ответ Future[String]. Поэтому вы можете сделать конструктор более универсальным, принимая в функции:

object Subject { 
    type Query = String 
    type DBResult = Future[String] 
    type DB : (Query) => DBResult 

    val defaultAuth : DBResult = Future.successful("auth:timeout_error") 

    val authQuery : Query = "auth" 

    def queryWithDefault(db : DB, default : DBResult = defaultAuth) : DB = 
    (query : Query) => db(query) fallbackTo default  
}//end object Subject 

class Subject(db : Subject.DB) extends Actor with ActorLogging { 

    override def preStart() : Unit = { 
    db(Subject.authQuery) pipeTo self 
    } 

    override def receive : Receive = { 
    case msg : String => log info msg 
    } 
}//end class Subject 

Теперь можно протестировать функцию queryWithDefault без использования Akka вообще:

import org.scalatest.{Matchers, WordSpecLike} 
import org.scalatest.concurrent.ScalaFutures 

class SubjectSpec 
    extends Matchers 
    with WorkSpecLike 
    with ScalaFutures { 

    val alwaysFail : DB = 
    (query : Query) => Future.failed(new Exception("always fails")) 

    import Subject.{defaultAuth, queryWithDefault, authQuery} 

    "queryWithDefault" should { 
    "always return default when db fails" in { 
     val db = queryWithDefault(alwaysFail, defaultAuth) 

     whenReady(
     for { 
      authQueryRes <- db(authQuery) 
      fooQueryRes <- db("foo") 
      defaultRes <- defaultAuth 
     }) { 
      authQueryRes shouldEqual defaultRes 
      fooQueryRes shouldEqual defaultRes 
     } 
    }//end "always return..." 
    }//end "queryWithDefault" should 
}//end class SubjectSpec 

Вы можете использовать переработан, и блок испытания, Subject.queryWithDefault Функция на производстве:

val actorDB : DB = (query : Query) => (db.ref ? query).mapTo[String] 

val subject = system.actorOf(Props(queryWithDefault(actorDB, defaultAuth))) 
1

Вы можете переопределить метод получить и отправить сообщение на TestProbe()

val probe = TestProbe() 

db.ref ! PoisonPill 

val actor = system.actorOf(Props(new Subject(db.ref) { 
    override def receive: Received = { 
     case message => probe.ref ! message 
    } 
})) 

probe.expectMsg("auth:timeout_error")