我再次邀请你与我分享你的智慧,scala padawan!
我正在使用scala中的反应性mongo,当我使用scalatest进行测试时,我遇到了以下问题。
首先是代码:
"delete" when {
"passing an existent id" should {
"succeed" in {
val testRecord = TestRecord(someString)
Await.result(persistenceService.persist(testRecord), Duration.Inf)
Await.result(persistenceService.delete(testRecord.id), Duration.Inf)
Thread.sleep(1000) // Why do I need that to make the test succeeds?
val thrownException = intercept[RecordNotFoundException] {
Await.result(persistenceService.read(testRecord.id), Duration.Inf)
}
thrownException.getMessage should include(testRecord._id.toString)
}
}
}
使用代码初始化连接到db(构造函数的一部分)的读取和删除方法:
class MongoPersistenceService[R](url: String, port: String, databaseName: String, collectionName: String) {
val driver = MongoDriver()
val parsedUri: Try[MongoConnection.ParsedURI] = MongoConnection.parseURI("%s:%s".format(url, port))
val connection: Try[MongoConnection] = parsedUri.map(driver.connection)
val mongoConnection = Future.fromTry(connection)
def db: Future[DefaultDB] = mongoConnection.flatMap(_.database(databaseName))
def collection: Future[BSONCollection] = db.map(_.collection(collectionName))
def read(id: BSONObjectID): Future[R] = {
val query = BSONDocument("_id" -> id)
val readResult: Future[R] = for {
coll <- collection
record <- coll.find(query).requireOne[R]
} yield record
readResult.recover {
case NoSuchResultException => throw RecordNotFoundException(id)
}
}
def delete(id: BSONObjectID): Future[Unit] = {
val query = BSONDocument("_id" -> id)
// first read then call remove. Read will throw if not present
read(id).flatMap { (_) => collection.map(coll => coll.remove(query)) }
}
}
因此,为了让我的测试通过,我必须在等待删除完成后立即使用Thread.sleep。知道这是邪恶的,通常受到许多鞭打的惩罚,我想学习并在这里找到适当的解决方案。
在尝试其他东西时,我发现而不是等待,完全关闭与数据库的连接也在做伎俩......
我在这里误解了什么?是否应该为每次调用打开和关闭与数据库的连接?而不是通过一个连接添加,删除,更新记录等许多操作?
请注意,当我删除删除功能中的读取调用时,一切正常。同样通过关闭连接,我的意思是从我的测试中调用MongoDriver上的close,并停止并重新开始嵌入我正在后台使用的Mongo。
感谢帮助人们。
答案 0 :(得分:2)
警告:这是一个盲目的猜测,我对Scala上的MongoDB没有经验。
您可能忘记了flatMap
看看这一点:
collection.map(coll => coll.remove(query))
由于您的代码的收集时间为Future[BSONCollection]
,而remove
会返回Future[WriteResult]
per doc,因此此表达式的实际类型为Future[Future[WriteResult]]
。
现在,您已将函数注释为返回Future[Unit]
。 Scala经常通过丢弃可能有意义的值来使Unit
作为返回值,这在你的情况下会这样做:
read(id).flatMap { (_) =>
collection.map(coll => {
coll.remove(query) // we didn't wait for removal
() // before returning unit
})
}
所以你的代码应该是
read(id).flatMap(_ => collection.flatMap(_.remove(query).map(_ => ())))
或for
- 理解:
for {
_ <- read(id)
coll <- collection
_ <- coll.remove(query)
} yield ()
您可以通过添加编译器标志(假设SBT)让Scala警告您丢弃的值:
scalacOptions += "-Ywarn-value-discard"