单元测试Scala应用程序https://www.playframework.com/documentation/2.4.x/ScalaTestingWithScalaTest的文档讨论了使用Mockito模拟数据库访问。虽然这种方法可以很好地测试从>数据库中获取信息的方法,但我没有看到如何测试插入,更新或删除数据的方法的明确解决方案。
这是我到目前为止设置的内容:
trait UserRepository { self: HasDatabaseConfig[JdbcProfile] =>
import driver.api._
class UserTable(tag: Tag) extends Table[userModel](tag, "users") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc )
def email = column[String]("email")
def * = (id.?, email) <> (userModel.tupled, userModel.unapply _)
}
def allUsers() : Future[Seq[userModel]]
def update(user: userModel) : Future[Int]
}
class SlickUserRepository extends UserRepository with HasDatabaseConfig[JdbcProfile] {
import driver.api._
protected val dbConfig = DatabaseConfigProvider.get[JdbcProfile](Play.current)
private val users = TableQuery[UserTable]
override def allUsers(): Future[Seq[userModel]] = {
db.run(users.result)
}
def update(user: userModel): Future[Int] = {
db.run(userTableQuery.filter(_.id === user.id).update(user))
}
}
class UserService(userRepository: UserRepository) {
def getUserById(id: Int): Future[Option[userModel]] = {
userRepository.allUsers().map { users =>
users.find(_.id.get == id)
}
// TODO, test this...
def updateUser(user: userModel): Future[Int] = {
userRepository.update(user)
}
}
然后我的测试:
class UserSpec extends PlaySpec with MockitoSugar with ScalaFutures {
"UserService" should {
val userRepository = mock[UserRepository]
val user1 = userModel(Option(1), "user1@test.com")
val user2 = userModel(Option(2), "user2@test.com")
// mock the access and return our own results
when(userRepository.allUsers) thenReturn Future {Seq(user1, user2)}
val userService = new UserService(userRepository)
"should find users correctly by id" in {
val future = userService.getUserById(1)
whenReady(future) { user =>
user.get mustBe user1
}
}
"should update user correctly" in {
// TODO test this
}
}
我想我需要模拟'update'方法并创建一个接收参数并更新模拟数据的存根。但是,我在Scala的技能是有限的,我无法绕过它。还有更好的方法吗?
谢谢!
答案 0 :(得分:0)
在这里我建议两个单元测试课程。一种用于测试UserService类中的逻辑。另一个测试逻辑,用于测试UserRepository类的逻辑(为此,使用扩展特性的虚拟测试类)。由于SlickUserRepository类具有其自己的测试覆盖范围,因此允许UserService测试类在其自己的测试中使用模拟[UserRepository]而不会降低覆盖范围,并且其测试仅关注其类的逻辑。
这样做确实简化了UserService测试,因此我不再赘述。
对于SlickUserRepository测试,我建议重组SlickUserRepository类中的逻辑。
我建议分离db.run内部的逻辑,并将其作为构造动作的单独方法。这样,您就可以针对“ db.run {}”内部的逻辑编写直接测试。
您会发现在更新方法中集成了db.run,因为它现在会损害您构造包含多个表调用的事务的能力。 DbAction需要链接在一起并在一个db.run(myDbAction.transactionally)中运行才能进行事务处理。这就是为什么我个人将db.run逻辑放在业务逻辑层中,而不是像在您的示例中那样直接在持久性层中。
在任何有db.run调用的地方,都可以将其作为单独的方法放置,这样您就可以轻松地窥探该调用:
def run[M](action: DBIO[M]): Future[M] = {
db.run(action)
}
未来不需要嘲笑。只需将这些定义为所需的结果即可:
Future.failed("your intanciated exception")
Future.success("your intanciated success class")