【问题标题】:Mocking database with Slick in ScalaTest + Mockito and testing UPDATE在 ScalaTest + Mockito 中使用 Slick 模拟数据库并测试 UPDATE
【发布时间】:2015-09-20 11:19:08
【问题描述】:

用于对 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
    }
}

我想我需要模拟“更新”方法并创建一个接受参数并更新模拟数据的存根。但是,我在 Scala 中的技能是有限的,我无法完全理解它。有没有更好的办法?

谢谢!

【问题讨论】:

  • 由于 Slick 基于 JDBC,您可以使用Acolyte framework 定义模拟/模拟连接进行测试。

标签: scala unit-testing mockito scalatest


【解决方案1】:

我在这里推荐两个单元测试类。一种用于测试 UserService 类中的逻辑。另一个测试 UserRepository 类逻辑的测试类(为此使用扩展特征的虚拟测试类)。由于 SlickUserRepository 类有自己的测试覆盖率,这允许 UserService 测试类在自己的测试中使用 mock[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")

【讨论】:

    猜你喜欢
    • 2014-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-05
    • 2018-03-21
    • 1970-01-01
    相关资源
    最近更新 更多