我正在尝试编写一种方法,它只是根据id从数据库中删除一行。
class PolicyHolderDAO(database: DatabaseDef) extends CRUDActor[PolicyHolder] {
private val policyHolders: TableQuery[PolicyHolderTable] = TableQuery[PolicyHolderTable]
implicit val system: ActorSystem = ActorSystem("Bitcoin-Insurance")
import system.dispatcher
implicit val timeout: Timeout = Timeout(5.seconds)
private implicit var session = database.createSession
override def receive = {
case PolicyHolderDAO.Read(id) => sender ! read(id)
case PolicyHolderDAO.Create(policyHolder) => sender ! create(policyHolder)
case PolicyHolderDAO.Delete(policyHolder) => sender ! delete(policyHolder)
}
/**
* @param policyHolder the policy holder to inserted into the database
* @return id
*/
override def create(policyHolder: PolicyHolder): Future[PolicyHolder] = {
future {
(policyHolders returning policyHolders.map(_.id) into
((policyHolder, id) => policyHolder.copy(id = Some(id)))) += policyHolder
}
}
/**
* @param id the id that corresponds to a policy holder
* @return a future value of a policy holder if the policy holder exists in the database, else it returns none
*/
override def read(id: Future[Long]): Future[Option[PolicyHolder]] = {
id.map(i => policyHolders.filter(p => p.id === i).firstOption)
}
/**
* @param policyHolder the policyHolder to be updated
* @return policyHolder the policyHolders information now saved in the database
*/
override def update(policyHolder: Future[PolicyHolder]): Future[Option[PolicyHolder]] = {
/* //val policyHolderFromDb = policyHolder.map(p => policyHolders.filter(_.id === p.id.getOrElse(-1)))
val policyHolderFromDb = for (p <- policyHolder; q = policyHolders.filter(_.id === p.id) ) yield q.update
val updatedPolicyHolder: Future[Option[PolicyHolder]] = for (p <- policyHolderFromDb; result = create(p)) yield result
updatedPolicyHolder*/
Future(None)
}
/**
* @param policyHolder the policy holder to be deleted from our database
* @return affectedRows the number of rows effected by this query
*/
override def delete(policyHolder: Future[PolicyHolder]): Future[Int] = {
val policyHolderId: Future[Long] = policyHolder.map(p => p.id.getOrElse(-1))
val affectedRows = for (id <- policyHolderId; q = policyHolders.filter(_.id === id)) yield q
affectedRows.map(q => q.delete)
}
}
我正在尝试使用为Scala测试编写的测试用例来测试它
"A PolicyHolderDAO Actor" must {
"be able to delete an existing policy holder from our database" in {
val policyHolder = PolicyHolder(None, "Chris", "Stewart")
val createdPolicyHolderAny: Future[Any] = policyHolderDAOActor ? PolicyHolderDAO.Create(policyHolder)
val createdPolicyHolder: Future[PolicyHolder] = createdPolicyHolderAny.mapTo[Future[PolicyHolder]].flatMap(p => p)
policyHolderDAOActor ! PolicyHolderDAO.Delete(createdPolicyHolder)
val deletedPolicyHolderAny: Future[Any] = policyHolderDAOActor ? PolicyHolderDAO.Get(createdPolicyHolder.map(_.id))
val deletedPolicyHolder: Future[Option[PolicyHolder]] = deletedPolicyHolderAny.mapTo[Future[Option[PolicyHolder]]].flatMap(p => p)
whenReady(deletedPolicyHolder, timeout(10 seconds), interval(5 millis)) { p =>
val policyHolderExists = p match {
case Some(a) =>
println(a)
true
case None => false
}
policyHolderExists must be(false)
}
}
}
但是我没有通过这个测试。原因是该行未从我们的数据库中删除。我不确定为什么这个测试失败了。我有一个相应的单元测试这个方法,由于删除返回1个受影响的行,这是有道理的。 Akka / Futures可能会有一些我不理解的东西吗?
谢谢!
答案 0 :(得分:1)
您的测试中可能存在竞争条件:
...
policyHolderDAOActor ! PolicyHolderDAO.Delete(createdPolicyHolder)
val deletedPolicyHolderAny: Future[Any] = policyHolderDAOActor ? PolicyHolderDAO.Get(createdPolicyHolder.map(_.id))
...
很难说,因为你没有公开Actor
这样做,但基于上面的delete
方法返回Future[Int]
的事实,似乎正在发生的事情是:
policyHolderDAOActor
在tell中传递删除消息。policyHolderDAOActor
按ID检索相同的记录。policyHolderDAOActor
调用delete
方法,该方法返回Future[Int]
,因此结果可能会被丢弃,receive
会立即返回。policyHolderDAOActor
然后可以自由处理下一条消息以按ID获取记录,但删除尚未完成处理,因此记录仍在那里。在上面两行之间引入等待可能使测试通过,但如果没有,或许共享一些Actor
代码会更有说服力。
如果其他消息的功能类似,那么当您尝试删除记录时,甚至还没有创建记录。上面的子弹仍然是正确的,只是采用不同的方法。为了安全起见,您应该阻止测试中的每个异步操作,以确保顺序处理。
答案 1 :(得分:0)
我认为您的问题在于将Slick会话传递到正确的位置:
(implicit val session: Session)
我不确定1),因为我习惯使用Slick with Play,这可能会有一些不同的API,但我有一些经验2):
答案 2 :(得分:0)
只是一个侧面评论,为什么不让delete
更简单?
def delete(policyHolder: Future[PolicyHolder]): Future[Int] = {
for (p <- policyHolder) yield policyHolders.delete(_.id === p.id)
}