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", "[email protected]", "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.

Hi Gerard,
Did you figured it out? I have similar issue.
Regards,
Pavel