### Dummy Notification Service Example Source: https://scalamock.org/introduction Demonstrates a Dummy test double implementation for a NotificationService. Dummies are passed around but not used, typically to fill parameter lists. ```scala trait NotificationService: def sendSms(phone: Phone): Future[Unit] object DummyNotificationService extends NotificationService: def sendSms(phone: Phone): Future[Unit] = Future.unit ``` -------------------------------- ### Fake User Repository Example Source: https://scalamock.org/introduction Illustrates a Fake test double implementation for a UserRepository. Fakes have working implementations but may take shortcuts, like an in-memory database, making them unsuitable for production. ```scala trait UserRepository: def create(user: User): Unit def find(userId: UserId): Option[User] class FakeUserRepository(users: Map[UserId, User]) extends UserRepository: def create(user: User): Future[Unit] = users += user.id -> user def find(userId: UserId): Future[Option[User]] = users.get(userId) ``` -------------------------------- ### ScalaMock Stubs with MUnit Source: https://scalamock.org/index Demonstrates using scalamock stubs with the munit testing framework in Scala. Includes setup for services and basic test structure for dependency injection. ```scala //> using test.dep org.scalamock::scalamock:7.3.2 //> using test.dep org.scalameta::munit:1.1.1 import org.scalamock.stubs.Stubs import munit.FunSuite trait Service1 trait Service2 class Service3(service1: Service1, service2: Service2) class MyTest extends FunSuite, Stubs: class Env: val service1 = stub[Service1] val service2 = stub[Service2] val service3 = Service3(service1, service2) test("do something"): val env = Env() // env.service1.someMethod.returnsWith(...) // env.service2.someMethod.returnsWith(...) // run tested method // assertEquals(env.service1.calls, List(...)) // assertEquals(env.service2.times, ...) ``` -------------------------------- ### scalamock Classic with ScalaTest Source: https://scalamock.org/index Example of using scalamock with ScalaTest for classic mocking. It demonstrates setting up mock objects for traits and classes, defining expectations, and verifying calls. ```Scala //> using test.dep org.scalamock::scalamock:7.3.2 //> using test.dep org.scalatest::scalatest:3.2.19 import org.scalamock.scalatest.MockFactory import org.scalatest.flatspec.AnyFlatSpec trait Service1 trait Service2 class Service3(service1: Service1, service2: Service2) class MyTest extends AnyFlatSpec, MockFactory: trait Wiring: val service1 = stub[Service1] val service2 = mock[Service2] val service3 = Service3(service1, service2) it should "do something" in new Wiring { // service1.someMethod.when(...).returns(...) // service2.someMethod.expects(...).returns(...) // run tested method // service1.someMethod.verify(...).once() } ``` -------------------------------- ### ETA Expansion Scala Example Source: https://scalamock.org/stubs/features Demonstrates how scalamock leverages ETA expansion to convert method selections into Scala functions. These functions are then implicitly converted into StubbedMethod representations, enabling mock configuration. ```scala class Foo: def noArgs: String = "" def oneArg(x: Int): String = "" def twoArgs(x: Int, y: String): String = "" val foo = Foo() val noArgsFun: () => String = () => foo.noArgs val oneArgFun: Int => String = foo.oneArg val twoArgsFun: (Int, String) => String = foo.twoArgs // In Scala 2 you should also add `_` to convert method with arguments to a function. E.G. `myObject.myMethod _` ``` -------------------------------- ### ScalaMock Stubs with ScalaTest Source: https://scalamock.org/index Shows how to integrate scalamock stubs with ScalaTest for mocking dependencies in Scala applications. Covers service stubbing and assertion examples. ```scala //> using test.dep org.scalamock::scalamock:7.3.2 //> using test.dep org.scalatest::scalatest:3.2.19 import org.scalamock.stubs.Stubs import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers trait Service1 trait Service2 class Service3(service1: Service1, service2: Service2) class MyTest extends AnyFunSuite, Matchers, Stubs: class Env: val service1 = stub[Service1] val service2 = stub[Service2] val service3 = Service3(service1, service2) test("do something"): val env = Env() // env.service1.someMethod.returnsWith(...) // env.service2.someMethod.returnsWith(...) // run tested method // env.service1.calls shouldBe List(...) // env.service2.times shouldBe ... ``` -------------------------------- ### scalamock Classic with specs2 Source: https://scalamock.org/index Example of using scalamock with specs2 for classic mocking. It shows how to integrate scalamock's MockContext with specs2 specifications to create and manage mock objects. ```Scala //> using test.dep org.scalamock::scalamock:7.3.2 //> using test.dep org.specs2::specs2-core:5.6.3 import org.scalamock.specs2.MockContext import org.specs2.mutable.Specification trait Service1 trait Service2 class Service3(service1: Service1, service2: Service2) class MySpec extends Specification { trait Wiring extends MockContext { val service1 = stub[Service1] val service2 = stub[Service2] val service3 = Service3(service1, service2) } "CoffeeMachine" should { "not turn on the heater when the water container is empty" in new Wiring { // service1.someMethod.when(...).returns(...) // service2.someMethod.expects(...).returns(...) // run tested method // service1.someMethod.verify(...).once() } } } ``` -------------------------------- ### Scalatest Integration with MockFactory Source: https://scalamock.org/classic Demonstrates how to integrate scalamock with Scalatest by mixing in MockFactory. This setup allows for creating stubs and mocks within your test suites for effective unit testing. ```scala //> using test.dep org.scalamock::scalamock:7.3.2 //> using test.dep org.scalatest::scalatest:3.2.19 import org.scalamock.scalatest.MockFactory import org.scalatest.flatspec.AnyFlatSpec // Define traits for dependencies trait Service1 trait Service2 case class Service3(s1: Service1, s2: Service2) class MyTest extends AnyFlatSpec, MockFactory: trait Wiring: val service1 = stub[Service1] val service2 = stub[Service2] val service3 = Service3(service1, service2) it should "do something" in new Wiring { // your test logic here, using service1, service2, service3 // Example: service1.someMethod _ expects() returning someValue } ``` -------------------------------- ### ScalaMock Stubs with ZIO Test Source: https://scalamock.org/index Explains how to use scalamock stubs with ZIO Test in Scala, leveraging ZIO's asynchronous capabilities. Includes examples for stubbing ZIO services and effectful assertions. ```scala //> using dep dev.zio::zio:2.1.17 //> using test.dep dev.zio::zio-test:2.1.17 //> using test.dep org.scalamock::scalamock-zio:7.3.2 import org.scalamock.stubs.ZIOStubs import zio.test.* trait Service1 trait Service2 class Service3(service1: Service1, service2: Service2) class MyTest extends ZIOSpecDefault, ZIOStubs: class Env: val service1 = stub[Service1] val service2 = stub[Service2] val service3 = Service3(service1, service2) override def spec: Spec[TestEnvironment & Scope, Any] = suite("tests")( test("do something") { val env = Env() /* for { _ <- env.service1.someMethod.succeedsWith(...) _ <- env.service2.someMethod.succeedsWith(...) result <- env.service3 logic to test } yield assertTrue( result == ..., env.service1.someMethod.calls == ..., env.service2.someMethod.times == ... ) */ } ) ``` -------------------------------- ### Define Scala Greetings Trait and Implementations Source: https://scalamock.org/stubs/features Defines the `Greetings` trait and its `Impl` class, along with `Formatter` traits for localization. This setup is used to demonstrate stubbing scenarios in ScalaMock. ```Scala trait Greetings: def sayHello(name: String): Unit object Greetings: class Impl(formatter: Formatter): def sayHello(name: String): Unit = println(formatter.format(name)) trait Formatter: def format(s: String): String object EnglishFormatter extends Formatter: def format(s: String): String = s"Hello $s" object JapaneseFormatter extends Formatter: def format(s: String): String = s"こんにちは $s" ``` -------------------------------- ### ETA Expansion in Scala Source: https://scalamock.org/classic/features Demonstrates how scalamock uses ETA expansion to convert method selections into functions, which are then implicitly converted to MockFunction or StubFunction representations. This example shows the conversion for methods with zero, one, and two arguments. ```Scala class Foo: def noArgs: String = "" def oneArg(x: Int): String = "" def twoArgs(x: Int, y: String): String = "" val foo = Foo() val noArgsFun: () => String = () => foo.noArgs val oneArgFun: Int => String = foo.oneArg val twoArgsFun: (Int, String) => String = foo.twoArgs // Note: In Scala 2, you would typically add '_' for methods with arguments, e.g., foo.oneArg _ ``` -------------------------------- ### Scala: Stubbing IO Results with returnsIOWith and returnsIO Source: https://scalamock.org/stubs/cats-effect Illustrates advanced stubbing for IO-returning methods using `returnsIOWith` and `returnsIO`. `returnsIOWith` allows setting a pre-defined IO, while `returnsIO` enables conditional stubbing based on method arguments, offering greater flexibility in test setups. ```Scala //> using dep org.typelevel::cats-effect:3.6.1 //> using test.dep org.scalamock::scalamock-cats-effect:7.3.2 //> using test.dep org.typelevel::munit-cats-effect:2.1.0 import org.scalamock.stubs.CatsEffectStubs import cats.effect.* import munit.* case class User(id: Long) trait UserService: def findUser(userId: Long): IO[Option[User]] class MySuite extends CatsEffectSuite, CatsEffectStubs: val userId = 100 val user = User(userId) test("returnsIOWith"): val userService = stub[UserService] for { _ <- userService.findUser.returnsIOWith(IO.pure(user)) result <- userService.findUser(userId) } yield assertEquals(result, user) test("returnsIO"): val userService = stub[UserService] for { _ <- userService.findUser.returnsIO: case `userId` => IO.pure(Some(user)) case _ => IO.none result1 <- userService.findUser(userId) result2 <- userService.findUser(userId + 1) } yield assertEquals((result1, result2), (Some(user), None)) ``` -------------------------------- ### StubbedMethod returnsWith Scala Example Source: https://scalamock.org/stubs/features Demonstrates using `returnsWith` to set a fixed return value for a stubbed method, regardless of the arguments passed. This is a common way to define mock behavior when argument-specific results are not needed. ```scala //> using test.dep org.scalamock::scalamock:7.3.2 //> using test.dep org.scalameta::munit:1.1.1 import org.scalamock.stubs.Stubs case class User(id: Long) trait UserService: def findUser(userId: Long): Option[User] class MySuite extends munit.FunSuite, Stubs: val userId = 100 val user = User(userId) test("findUserExample"): val userService = stub[UserService] userService.findUser.returnsWith(Some(user)) assertEquals(userService.findUser(userId), Some(user)) ``` -------------------------------- ### NotImplementedError Scala Example Source: https://scalamock.org/stubs/features Shows the behavior when a stubbed method is invoked without its result being configured. It throws a `NotImplementedError` with a descriptive message indicating the missing implementation. ```scala //> using test.dep org.scalamock::scalamock:7.3.2 //> using test.dep org.scalameta::munit:1.1.1 import org.scalamock.stubs.Stubs case class User(id: Long) trait UserService: def findUser(userId: Long): Option[User] class MySuite extends munit.FunSuite, Stubs { test("findUserExample"): val userService = stub[UserService] userService.findUser(100) } ``` -------------------------------- ### ScalaMock Stubs with Specs2 Source: https://scalamock.org/index Illustrates the usage of scalamock stubs within the specs2 testing framework for Scala. Demonstrates setting up stubs for services and writing tests. ```scala //> using test.dep org.scalamock::scalamock:7.3.2 //> using test.dep org.specs2::specs2-core:5.6.3 import org.scalamock.stubs.Stubs import org.specs2.mutable.Specification trait Service1 trait Service2 class Service3(service1: Service1, service2: Service2) class MySpec extends Specification, Stubs: class Env: val service1 = stub[Service1] val service2 = stub[Service2] val service3 = Service3(service1, service2) "do something" should { "work" in { val env = Env() // env.service1.someMethod.returnsWith(...) // env.service2.someMethod.returnsWith(...) // run tested method // env.service1.calls check // env.service2.times check } } ``` -------------------------------- ### Test UserAuthService with Scalamock and Munit (Scala) Source: https://scalamock.org/examples Provides comprehensive test cases for `UserAuthService` using `scalamock` for mocking its dependencies (`UserService`, `PasswordService`) and `munit-cats-effect` for testing `IO` effects. It covers six distinct scenarios including happy path, user not found, user blocked, password mismatch, and exceptions during dependency calls. ```scala //> using dep org.typelevel::cats-effect:3.6.1 //> using test.dep org.scalamock::scalamock-cats-effect:7.3.2 //> using test.dep org.typelevel::munit-cats-effect:2.1.0 import org.scalamock.stubs.CatsEffectStubs import cats.effect.* import munit.* object UserAuthServiceSpec extends CatsEffectSuite, CatsEffectStubs { // fixture class Env { val userService = stub[UserService] val passwordService = stub[PasswordService] val authService = UserAuthService(userService, passwordService) } // test data val userId = 100 val user = User(userId, UserStatus.Normal) val blockedUser = User(userId, UserStatus.Blocked) val password = "password" test("happy path") { val env = Env() for { _ <- env.userService.findUser.succeedsWith(Some(user)) _ <- env.passwordService.checkPassword.succeedsWith(true) result <- env.authService.authorize(userId, password) } yield assertEquals(result, Right(())) } test("user not found") { val env = Env() for { _ <- env.userService.findUser.succeedsWith(None) result <- env.authService.authorize(userId, password) } yield assertEquals(result, Left(FailedAuthResult.UserNotFound)) } test("user blocked") { val env = Env() for { _ <- env.userService.findUser.succeedsWith(Some(blockedUser)) result <- env.authService.authorize(userId, password) } yield assertEquals(result, Left(FailedAuthResult.UserNotAllowed)) } test("user search failed") { val env = Env() val ex = new RuntimeException("test") for { _ <- env.userService.findUser.raisesErrorWith(ex) result <- env.authService.authorize(userId, password).attempt } yield assertEquals(result, Left(ex)) } test("password check failed") { val env = Env() val ex = new RuntimeException("test") for { _ <- env.userService.findUser.succeedsWith(Some(user)) _ <- env.passwordService.checkPassword.raisesErrorWith(ex) result <- env.authService.authorize(userId, password).attempt } yield assertEquals(result, Left(ex)) } test("wrong password") { val env = Env() for { _ <- env.userService.findUser.succeedsWith(Some(user)) _ <- env.passwordService.checkPassword.succeedsWith(false) result <- env.authService.authorize(userId, password) } yield assertEquals(result, Left(FailedAuthResult.WrongPassword)) } } ``` -------------------------------- ### Scala UserAuthService and Dependencies Source: https://scalamock.org/examples Defines the core Scala components including data structures like User and UserStatus, service traits for UserService and PasswordService, and the UserAuthService implementation. This service orchestrates user lookup and password verification. ```Scala import scala.concurrent.Future enum UserStatus { case Normal, Blocked } enum FailedAuthResult { case UserNotFound, UserNotAllowed, WrongPassword } case class User(id: Long, status: UserStatus) trait UserService { def findUser(userId: Long): Future[Option[User]] } trait PasswordService { def checkPassword(id: Long, password: String): Future[Boolean] } class UserAuthService( userService: UserService, passwordService: PasswordService ) { def authorize(id: Long, password: String): Future[Either[FailedAuthResult, Unit]] = userService.findUser(id).flatMap { case None => Future.successful(Left(FailedAuthResult.UserNotFound)) case Some(user) if user.status == UserStatus.Blocked => Future.successful(Left(FailedAuthResult.UserNotAllowed)) case Some(user) => passwordService.checkPassword(id, password).map { case true => Right(()) case false => Left(FailedAuthResult.WrongPassword) } } } ``` -------------------------------- ### Convert Method With Context Parameters to Function Source: https://scalamock.org/faq Demonstrates converting Scala methods with context parameters (using `using`) into functions. The conversion requires explicitly specifying the context parameter in the function signature. ```scala class Context: def context(x: Int)(using String): String = "" val ctx = Context() val x = ctx.context(_: Int)(using _: String) // (Int, String) => String // In Scala 2, implicit parameter is equivalent to plain one, so you don’t need to specify `implicit` explicitly ``` -------------------------------- ### Generating a Stub Scala Example Source: https://scalamock.org/stubs/features Illustrates how to generate a stub object for a given trait using the `stub[T]` method. The generated stub is a `Stub[T]` type, which is a subtype of the original trait, allowing for method mocking. ```scala //> using test.dep org.scalamock::scalamock:7.3.2 //> using test.dep org.scalameta::munit:1.1.1 import org.scalamock.stubs.Stubs trait MyTrait class MySpec extends munit.FunSuite, Stubs { test("my test case"): val myTrait: Stub[MyTrait] = stub[MyTrait] } ``` -------------------------------- ### scalamock ZIO: returnsZIOWith, returnsZIO Source: https://scalamock.org/stubs/zio Explains advanced methods for stubbing ZIO effects in scalamock: `returnsZIOWith` for setting a ZIO effect directly, and `returnsZIO` for setting a ZIO effect based on method arguments, useful for abstracting stubbing logic. ```Scala //> 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) } ) ``` -------------------------------- ### Scalamock ZIO Tests for UserAuthService Source: https://scalamock.org/examples A ZIO test suite using Scalamock to verify the UserAuthService. It covers happy path and various error conditions like user not found, user blocked, invalid password, and service exceptions, demonstrating dependency mocking with ZIOStubs. ```scala //> using dep dev.zio::zio:2.1.17 //> using test.dep dev.zio::zio-test:2.1.17 //> using test.dep org.scalamock::scalamock-zio:7.3.2 import org.scalamock.stubs.ZIOStubs import zio.test.* object UserAuthServiceSpec extends ZIOSpecDefault, ZIOStubs { // fixture class Env { val userService = stub[UserService] val passwordService = stub[PasswordService] val authService = UserAuthService(userService, passwordService) } // test data val userId = 100 val user = User(userId, UserStatus.Normal) val blockedUser = User(userId, UserStatus.Blocked) val password = "password" override def spec: Spec[TestEnvironment & Scope, Any] = suite("authorize")( test("happy path") { val env = Env() for { _ <- env.userService.findUser.succeedsWith(Some(user)) _ <- env.passwordService.checkPassword.succeedsWith(true) result <- env.authService.authorize(userId, password).either } yield assertTrue(result == Right(())) }, test("user not found") { val env = Env() for { _ <- env.userService.findUser.succeedsWith(None) result <- env.authService.authorize(userId, password).either } yield assertTrue(result == Left(FailedAuthResult.UserNotFound)) }, test("user blocked") { val env = Env() for { _ <- env.userService.findUser.succeedsWith(Some(blockedUser)) result <- env.authService.authorize(userId, password).either } yield assertTrue(result == Left(FailedAuthResult.UserNotAllowed)) }, test("user search failed") { val env = Env() val ex = new RuntimeException("test") for { _ <- env.userService.findUser.failsWith(ex) result <- env.authService.authorize(userId, password).either } yield assertTrue(result == Left(ex)) }, test("password check failed") { val env = Env() val ex = new RuntimeException("test") for { _ <- env.userService.findUser.succeedsWith(Some(user)) _ <- env.passwordService.checkPassword.failsWith(ex) result <- env.authService.authorize(userId, password).either } yield assertTrue(result == Left(ex)) }, test("wrong password") { val env = Env() for { _ <- env.userService.findUser.succeedsWith(Some(user)) _ <- env.passwordService.checkPassword.succeedsWith(false) result <- env.authService.authorize(userId, password).either } yield assertTrue(result == Left(FailedAuthResult.WrongPassword)) } ) } ``` -------------------------------- ### scalamock Dependencies Source: https://scalamock.org/index Instructions for adding scalamock as a test dependency across various Scala build tools, including sbt, Mill, Scala-CLI, and Maven. The core module and integrations for ZIO and cats-effect are listed. ```sbt libraryDependencies ++= Seq( // core module "org.scalamock" %% "scalamock" % "" % Test, // zio integration "org.scalamock" %% "scalamock-zio" % "" % Test, // cats-effect integration "org.scalamock" %% "scalamock-cats-effect" % "" % Test ) ``` ```Mill object main extends JavaModule { object test extends JavaModuleTests { override def ivyDeps = Agg( ivy"org.scalamock::scalamock:", // zio integration ivy"org.scalamock::scalamock-zio:", // cats-effect integration ivy"org.scalamock::scalamock-cats-effect:" ) } } ``` ```Scala-CLI //> using test.dep "org.scalamock::scalamock:" //> using test.dep "org.scalamock::scalamock-zio:" //> using test.dep "org.scalamock::scalamock-cats-effect:" ``` ```Maven org.scalamock scalamock_3 test org.scalamock scalamock-zio_3 test org.scalamock scalamock-cats-effect_3 test ``` -------------------------------- ### ScalaMock Stubs with MUnit-Cats-Effect Source: https://scalamock.org/index Demonstrates integrating scalamock stubs with munit-cats-effect for testing Scala applications that use Cats Effect. Shows stubbing and effectful assertions. ```scala //> using dep org.typelevel::cats-effect:3.6.1 //> using test.dep org.scalamock::scalamock-cats-effect:7.3.2 //> using test.dep org.typelevel::munit-cats-effect:2.1.0 import org.scalamock.stubs.CatsEffectStubs import cats.effect.* import munit.* trait Service1 trait Service2 class Service3(service1: Service1, service2: Service2) class MyTest extends CatsEffectSuite, CatsEffectStubs: class Env: val service1 = stub[Service1] val service2 = stub[Service2] val service3 = Service3(service1, service2) test("do something"): val env = Env() /* for { _ <- env.service1.someMethod.succeedsWith(...) _ <- env.service2.someMethod.succeedsWith(...) result <- env.service3 logic to test } yield { assertEquals(result, ...) assertEquals(env.service1.someMethod.calls, ...) assertEquals(env.service2.someMethod.times, ...) } */ ``` -------------------------------- ### Define UserAuthService with Dependencies (Scala) Source: https://scalamock.org/examples Defines the `UserAuthService` class, which depends on `UserService` and `PasswordService` traits. It utilizes `cats.effect.IO` for asynchronous operations and outlines the `authorize` method's logic for user authentication, handling different user statuses and password checks. ```scala //> using dep org.typelevel::cats-effect:3.6.1 //> using test.dep org.scalamock::scalamock-cats-effect:7.3.2 //> using test.dep org.typelevel::munit-cats-effect:2.1.0 import cats.effect.IO enum UserStatus { case Normal, Blocked } enum FailedAuthResult { case UserNotFound, UserNotAllowed, WrongPassword } case class User(id: Long, status: UserStatus) trait UserService { def findUser(userId: Long): IO[Option[User]] } trait PasswordService { def checkPassword(id: Long, password: String): IO[Boolean] } class UserAuthService( userService: UserService, passwordService: PasswordService ) { def authorize(id: Long, password: String): IO[Either[FailedAuthResult, Unit]] = userService.findUser(id).flatMap { case None => IO.pure(Left(FailedAuthResult.UserNotFound)) case Some(user) if user.status == UserStatus.Blocked => IO.pure(Left(FailedAuthResult.UserNotAllowed)) case Some(user) => passwordService.checkPassword(id, password).map { case true => Right(()) case false => Left(FailedAuthResult.WrongPassword) } } } ``` -------------------------------- ### ScalaMock: Verify Method Invocation Order with isBefore/isAfter Source: https://scalamock.org/stubs/features Explains how to check the order of method invocations using `isBefore` and `isAfter`. This requires defining `CallLog` as a context parameter before stub generation. ```scala //> using test.dep org.scalamock::scalamock:7.3.2 //> using test.dep org.scalameta::munit:1.1.1 import org.scalamock.stubs.* trait MyService: def firstMethod(id: Long): Option[String] def secondMethod(phone: String): Unit class MySuite extends munit.FunSuite, Stubs { test("my test case") { given CallLog = CallLog() val myService = stub[MyService] myService.firstMethod.returnsWith(Some("some result")) myService.secondMethod.returnsWith(()) assertEquals(myService.secondMethod.isBefore(myService.firstMethod), false) assertEquals(myService.secondMethod.isAfter(myService.firstMethod), true) } } ``` -------------------------------- ### scalamock ZIO: succeedsWith, failsWith, diesWith Source: https://scalamock.org/stubs/zio Demonstrates how to stub methods returning ZIO effects using scalamock's ZIO integration. It covers `succeedsWith` for successful values, `failsWith` for failed values, and `diesWith` for defects, all returning ZIO for convenient use within for comprehensions. ```Scala //> 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)) } } ) ``` -------------------------------- ### Scala UserAuthService Definition Source: https://scalamock.org/examples Defines the core Scala case classes, enums, traits, and the UserAuthService class. It outlines the dependencies (UserService, PasswordService) and the authorization logic, including potential failure outcomes. ```scala import zio.* enum UserStatus { case Normal, Blocked } enum FailedAuthResult { case UserNotFound, UserNotAllowed, WrongPassword } case class User(id: Long, status: UserStatus) trait UserService { def findUser(userId: Long): Task[Option[User]] } trait PasswordService { def checkPassword(id: Long, password: String): Task[Boolean] } class UserAuthService( userService: UserService, passwordService: PasswordService ) { def authorize(id: Long, password: String): IO[FailedAuthResult | Throwable, Unit] = userService.findUser(id).flatMap { case None => ZIO.fail(FailedAuthResult.UserNotFound) case Some(user) if user.status == UserStatus.Blocked => ZIO.fail(FailedAuthResult.UserNotAllowed) case Some(user) => passwordService.checkPassword(id, password) .filterOrFail(identity)(FailedAuthResult.WrongPassword) .unit } } ``` -------------------------------- ### UserAuthService Definition Source: https://scalamock.org/examples Defines the UserAuthService, its dependencies (UserService, PasswordService), and the authorize method. It handles user lookup, status checks, and password verification, returning a Future of Either for success or failure results. ```scala import scala.concurrent.Future enum UserStatus { case Normal, Blocked } enum FailedAuthResult { case UserNotFound, UserNotAllowed, WrongPassword } case class User(id: Long, status: UserStatus) trait UserService { def findUser(userId: Long): Future[Option[User]] } trait PasswordService { def checkPassword(id: Long, password: String): Future[Boolean] } class UserAuthService( userService: UserService, passwordService: PasswordService ) { def authorize(id: Long, password: String): Future[Either[FailedAuthResult, Unit]] = userService.findUser(id).flatMap { case None => Future.successful(Left(FailedAuthResult.UserNotFound)) case Some(user) if user.status == UserStatus.Blocked => Future.successful(Left(FailedAuthResult.UserNotAllowed)) case Some(user) => passwordService.checkPassword(id, password).map { case true => Right(()) case false => Left(FailedAuthResult.WrongPassword) } } } ``` -------------------------------- ### Configure Mock Return Values and Exceptions Source: https://scalamock.org/classic/features Demonstrates various ways to configure the behavior of a mocked method. This includes setting a specific return value using `returns` or `returning`, specifying an exception to be thrown using `throwing` or `throws`, and defining a custom behavior based on arguments using `onCall`. ```Scala myTrait.twoArgs.expects(1, "hello").returns("world") myTrait.twoArgs.expects(1, "hello").returning("world") myTrait.twoArgs.expects(1, "hello").throwing(new RuntimeException("world")) myTrait.twoArgs.expects(1, "hello").throws(new RuntimeException("world")) myTrait.twoArgs.expects(1, "hello").onCall { (x: Int, y: String) => s"world, $y-$x" } myTrait.twoArgs.expects(1, "hello").onCall { (x: Int, y: String) => if (y.length != x) throw new RuntimeException("not hello") else "world" } ``` -------------------------------- ### Generate Mock Object Source: https://scalamock.org/classic/features Shows how to generate a mock object for a given trait using the `mock` function. This is the first step in defining expectations for the mocked trait's methods. ```Scala trait MyTrait: def twoArgs(x: Int, y: String): String def oneArg(x: Int): String def noArgs: String val myTrait = mock[MyTrait] ``` -------------------------------- ### ScalaMock: Set Method Results with returns Source: https://scalamock.org/stubs/features Demonstrates how to set method results based on arguments using the `returns` syntax in ScalaMock. This is useful for creating stubs that behave differently based on input parameters. ```scala //> using test.dep org.scalamock::scalamock:7.3.2 //> using test.dep org.scalameta::munit:1.1.1 import org.scalamock.stubs.* case class User(id: Long) trait UserService: def findUser(userId: Long): Option[User] class MySuite extends munit.FunSuite, Stubs: val knownUserId = 100 val unknownUserId = -1 val user = User(userId) val userService: Stub[UserService] = stub[UserService] userService.findUser.returns: case `knownUserId` => Some(user) case _ -> None override def beforeEach(context: BeforeEach): resetStubs() test("return user for known user id"): assertEquals(userService.findUser(knownUserId), Some(user)) test("not return user for unknown user id"): assertEquals(userService.findUser(unknownUserId), None) ``` -------------------------------- ### Scalatest Async Integration with AsyncMockFactory Source: https://scalamock.org/classic Shows how to use scalamock with Scalatest for asynchronous tests using AsyncMockFactory. This is suitable for testing code that returns Futures, ensuring proper mock behavior in concurrent scenarios. ```scala import org.scalamock.scalatest.AsyncMockFactory import org.scalatest.flatspec.AsyncFlatSpec import scala.concurrent.Future // Assume Currency and ExchangeRateListing are defined elsewhere case class Currency(id: String, valueToUSD: Double, change: Double) trait CurrencyDatabase { def getCurrency(id: String): Currency } class ExchangeRateListing(currencyDatabase: CurrencyDatabase) { def getExchangeRate(fromCurrencyId: String, toCurrencyId: String): Future[Double] = { val from = currencyDatabase.getCurrency(fromCurrencyId) val to = currencyDatabase.getCurrency(toCurrencyId) Future.successful(from.valueToUSD / to.valueToUSD) } } class ExchangeRateListingTest extends AsyncFlatSpec with AsyncMockFactory { val eur = Currency(id = "EUR", valueToUSD = 1.0531, change = -0.0016) val gpb = Currency(id = "GPB", valueToUSD = 1.2280, change = -0.0012) val aud = Currency(id = "AUD", valueToUSD = 0.7656, change = -0.0024) "ExchangeRateListing" should "eventually return the exchange rate between passed Currencies when getExchangeRate is invoked" in { val currencyDatabaseStub = stub[CurrencyDatabase] currencyDatabaseStub.getCurrency.when(eur.id).returns(eur) currencyDatabaseStub.getCurrency.when(gpb.id).returns(gpb) currencyDatabaseStub.getCurrency.when(aud.id).returns(aud) val listing = new ExchangeRateListing(currencyDatabaseStub) val future: Future[Double] = listing.getExchangeRate(eur.id, gpb.id) future.map(exchangeRate => assert(exchangeRate == eur.valueToUSD / gpb.valueToUSD)) } } ``` -------------------------------- ### Specs2 Integration with MockContext Source: https://scalamock.org/classic Illustrates how to integrate scalamock with Specs2 by mixing in MockContext. This approach is used for creating mocks and stubs within Specs2 specifications, enabling behavior-driven development. ```scala //> using test.dep org.scalamock::scalamock:7.3.2 //> using test.dep org.specs2::specs2-core:5.6.3 import org.scalamock.specs2.MockContext import org.specs2.mutable.Specification // Define traits for dependencies trait Service1 trait Service2 case class Service3(s1: Service1, s2: Service2) trait WaterContainer { def isEmpty: Boolean } class MySpec extends Specification { trait Wiring extends MockContext { val service1 = stub[Service1] val service2 = stub[Service2] val service3 = Service3(service1, service2) } "CoffeeMachine" should { "not turn on the heater when the water container is empty" in new Wiring { val waterContainerMock = mock[WaterContainer] waterContainerMock.isEmpty.expects().returning(true) // Further test logic here... } } } ``` -------------------------------- ### Argument Matchers for Verification (Scala) Source: https://scalamock.org/classic/features Utilizes argument matchers for flexible verification: exact matchers (literals, variables), wildcard `*` for any value, epsilon `~` for floating-point comparisons within a range, and predicate `where` for custom logic. ```Scala // expects exact arguments "foo" and 42 myMock.someMethod.verify("foo", 42).once() // expects exact argument "foo" and any second argument myMock.someMethod.verify("foo", *).once() // expects exact argument "foo" and number close to 42.0 within 0.001 range // 42.0001 will pass and 42.01 will fail myMock.otherMethod.verify("foo", ~42.0).once() // def someMethod(x: Int, y: Int): Unit // expects two arguments, where first is less than second myMock.someMethod.verify(where { (x, y) => x < y }).once() ```