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.