ZIO integration
Before reading this - please read Features page.
The main difference is - you should mixin org.scalamock.stubs.ZIOStubs
interface instead of org.scalamock.stubs.Stubs
.
Dependency can be found on Home Page
It provides:
- implicit conversions to StubbedZIOMethod instead of StubbedMethod
- StubIO instance, which provides support for functional effect to work correctly
StubbedZIOMethod
StubbedZIOMethod
is a subtype of StubbedMethod
, which adds some convenient methods returning ZIO.
For method without arguments returning ZIO - you can omit creating a function from
()
. So instead of() => myObj.myMethod
just usemyObj.myMethod
succeedsWith, failsWith, diesWith
- succeedsWith - returns ZIO with a successful value
- failsWith - returns ZIO with a failed value
- diesWith - returns ZIO with a defect
All these methods return ZIO itself for convenient use in for comprehensions.
These methods are the default way of setting a result.
//> using dep dev.zio::zio:2.1.17
//> using test.dep org.scalamock::scalamock-zio:7.3.2
//> using test.dep dev.zio::zio-test:2.1.17
import org.scalamock.stubs.ZIOStubs
import zio.*
import zio.test.*
case class User(id: Long)
trait UserService:
def findUser(userId: Long): IO[None.type, User]
class MySuite extends ZIOSpecDefault, ZIOStubs:
val userId = 100
val user = User(userId)
override def spec: Spec[TestEnvironment & Scope, Any] =
suite("tests")(
test("succeedsWith") {
val userService = stub[UserService]
for {
_ <- userService.findUser.succeedsWith(user)
result <- userService.findUser(userId)
} yield assertTrue(result == user)
},
test("failsWith") {
val userService = stub[UserService]
for {
_ <- userService.findUser.failsWith(None)
result <- userService.findUser(userId).either
} yield assertTrue(result == Left(None))
},
test("diesWith") {
val userService = stub[UserService]
val exception = new RuntimeException("test")
for {
_ <- userService.findUser.diesWith(exception)
exit <- userService.findUser(userId).exit
} yield exit match {
case Exit.Success(_) => assertTrue(false)
case Exit.Failure(cause) => assertTrue(cause.defects.contains(exception))
}
}
)
returnsZIO and returnsZIOWith
If above methods are not enough - you can use:
- returnsZIOWith - allows to set result which type should match with method return type. Not only ZIO can be set here, but this method returns ZIO.
- returnsZIO - allows to set result depending on method arguments. This can be useful if you abstract over your tests or create your stubs per suite.
//> using dep dev.zio::zio:2.1.17
//> using test.dep org.scalamock::scalamock-zio:7.3.2
//> using test.dep dev.zio::zio-test:2.1.17
import org.scalamock.stubs.ZIOStubs
import zio.*
import zio.test.*
case class User(id: Long)
trait UserService:
def findUser(userId: Long): IO[None.type, User]
class MySuite extends ZIOSpecDefault, ZIOStubs:
val userId = 100
val user = User(userId)
override def spec: Spec[TestEnvironment & Scope, Any] =
suite("tests")(
test("returnsZIOWith") {
val userService = stub[UserService]
for {
_ <- userService.findUser.returnsZIOWith(ZIO.succeed(User(userId)))
result <- userService.findUser(userId)
} yield assertTrue(result == user)
},
test("returnsZIO") {
val userService = stub[UserService]
for {
_ <- userService.findUser.returnsZIO:
case `userId` => ZIO.succeed(user)
case _ => ZIO.fail(None)
result <- userService.findUser(userId)
} yield assertTrue(result == user)
}
)
timesZIO and callsZIO
Same methods as times and calls, but return ZIO, added for convenience, you can still just use times and calls, it is fully thread-safe.