Mocking database for unit tests with Slick

Hi folks,

I’m currently writing unit tests for some Scala Data Access Objects that use Slick (3.3.3).

I don’t want to write integration tests at this stage and so I’m attempting to mock the database using ScalaMock.

However when I try to mock a query hitting the database, I run into this issue:

overloaded method value expects with alternatives:
  (matcher: org.scalamock.function.FunctionAdapter1[slick.dbio.DBIOAction[Nothing,slick.dbio.NoStream,Nothing],Boolean])org.scalamock.handlers.CallHandler1[slick.dbio.DBIOAction[Nothing,slick.dbio.NoStream,Nothing],scala.concurrent.Future[Nothing]] <and>
  (v1: org.scalamock.matchers.MockParameter[slick.dbio.DBIOAction[Nothing,slick.dbio.NoStream,Nothing]])org.scalamock.handlers.CallHandler1[slick.dbio.DBIOAction[Nothing,slick.dbio.NoStream,Nothing],scala.concurrent.Future[Nothing]]
 cannot be applied to (slick.jdbc.PostgresProfile.StreamingProfileAction[Seq[com.foram.models.UsersTable#TableElementType],com.foram.models.UsersTable#TableElementType,slick.dbio.Effect.Read])
      (mockDB.run _).expects(usersQuery).returning(Future(Seq(sampleUser)))

I suspect I need some implicit conversion to convert the query into a DBIOAction but can’t figure out how to.

This is what the DAO looks like:

package com.foram.dao

import com.foram.auth.Auth
import com.foram.models.{User, UsersTable}
import slick.jdbc.PostgresProfile
import slick.jdbc.PostgresProfile.api._
import slick.lifted.TableQuery

import java.util.UUID
import scala.concurrent.Future

class UsersDao(db: PostgresProfile.backend.Database) extends AbstractUsersDao {
  def findAll: Future[Seq[User]] = db.run(users.sortBy(_.createdAt.asc).result)

  def findById(id: UUID): Future[User] = db.run(users.filter(_.id === id).result.head)

  def findByUsername(username: String): Future[User] = db.run(users.filter(_.username === username).result.head)

  def create(user: User): Future[UUID] = db.run(users.returning(users.map(_.id)) += Auth.hashUserPassword(user))

  def update(id: UUID, user: User): Future[Int] = db.run(users.filter(_.id === user.id).update(user))

  def delete(id: UUID): Future[Int] = db.run(users.filter(_.id === id).delete)
}

// Trait for mocking purposes
trait AbstractUsersDao {
  val users = TableQuery[UsersTable]

  def findAll: Future[Seq[User]]

  def findById(id: UUID): Future[User]

  def findByUsername(username: String): Future[User]

  def create(user: User): Future[UUID]

  def update(id: UUID, user: User): Future[Int]

  def delete(id: UUID): Future[Int]
}

And this is the current state of the unit tests:

package com.foram.dao

import com.foram.models.User
import org.scalamock.scalatest.MockFactory
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import slick.jdbc.PostgresProfile
import slick.jdbc.PostgresProfile.api._
import slick.lifted.{Query, TableQuery}

import java.time.OffsetDateTime
import java.util.UUID.randomUUID
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

class UsersDaoSpec extends AnyWordSpec with Matchers with ScalaFutures with MockFactory {
  def mockDB = mock[PostgresProfile.backend.Database]

  val sampleUser: User = User(randomUUID(), "Quincy Lars", "quincy", "qlars@example.com", "password123", "admin", OffsetDateTime.now(), OffsetDateTime.now())

  "UsersDao" should {
    "return a Seq of Users from findAll" in {
      val usersDao = new UsersDao(mockDB)

      val usersQuery = usersDao.users.sortBy(_.createdAt.asc).result

      (mockDB.run _).expects(usersQuery).returning(Future(Seq(sampleUser)))

      val usersFuture = usersDao.findAll

      usersFuture.futureValue shouldBe Seq(sampleUser)
    }
  }
}

The full code for the project is in this GitHub repo.

I’d be very grateful if anyone could point me in the right direction.