在我的play framework(2.5)app中,我需要为服务编写单元测试。
我需要隔离数据访问逻辑,以便能够单独测试服务层, 为此,我想创建存储库接口并在我的单元测试中对它们进行MOCK:
class UserService {
def signUpNewUser(username: String, memberName: String): Future[Unit] {
val userId = 1 // Set to 1 for demo
val user = User(userId, username)
val member = Member(memberName, userId)
// ---- I NEED TO EXECUTE THIS BLOCK WITHIN TRANSACTION ----
for {
userResult <- userRepository.save(user)
memberRepository.save(member)
} yield ()
// ---- END OF TRANSACTION ----
}
}
在上面的示例中,应在事务中执行userRepository.save(User)
和memberRepository.save(member)
操作。
我不想直接在我的服务层使用光滑,因为它会使我的测试复杂化。
另外,我不想在我的单元测试中使用嵌入式数据库,在其他地方它将是一个NOT单元测试,我需要完全隔离。
我不希望我的存储库接口完全依赖于光滑,但是需要这样的东西:
trait UserRepository {
findById(id: Long): Future[Option[User]]
save(user: User): Future[Unit]
}
如何通过光滑实现这一目标?
答案 0 :(得分:4)
好的 - 让我们将您的问题分解为三个部分。
如何在交易中执行阻止
基本上阅读这个答案:How to use transaction in slick
只要您将DBIO
转换为Future
,您就完成了。没有机会在单个交易中组成多个操作。故事结束。
如何避免在测试中使用Slick
这基本上是一个设计问题 - 如果您希望在Repository
/ DAO
/之上设置业务层,而不是让此服务层处理事务。您不需要在此图层之外与Slick
进行互动。
避免您的存储库界面依赖于Slick
以最简单的方式 - 您需要依赖Slick DBIO
来组成事务中的操作(并且在事务中编写Repository
方法是在任何严肃的应用程序中都无法避免的。)
如果您想避免依赖DBIO
,您可能会创建自己的monadic类型,例如TransactionBoundary[T]
或TransactionContext[T]
。
然后你会有TransactionManager
之类的东西来执行这个TransactionContext[T]
。
恕我直言不值得努力,我只是简单地使用DBIO
已经有一个很棒的名字(比如Haskell&#39; IO
monad - DBIO
告诉你你描述了对您的存储执行的IO
操作。但是,让我们假设你仍然想避免它。
你可以这样做:
package transaction {
object Transactions {
implicit class TransactionBoundary[T](private[transaction] val dbio: DBIO[T]) {
// ...
}
}
class TransactionManager {
def execute[T](boundary: TransactionBoundary[T]): Future[T] = db.run(boundary.dbio)
}
}
你的特质看起来像这样:
trait UserRepository {
findById(id: Long): TransactionBoundary[Option[User]]
save(user: User): TransactionBoundary[Unit]
}
在你的代码中的某个地方,你会这样做:
transactionManager.execute(
for {
userResult <- userRepository.save(user)
memberRepository.save(member)
} yield ()
)
通过使用隐式转换,您可以将Repository
中的方法结果自动转换为TransactionBoundary
。
但是再次 - 恕我直言以上所有这些并没有带来任何实际优势而不是使用DBIO
(除了美学的味道)。如果您想避免在某个图层之外使用Slick
相关类,只需创建一个类型别名:
type TransactionBoundary[T] = DBIO[T]
并在任何地方使用它。