Scaladex | Scaladoc | Maven |
---|---|---|
A small library to help you (or not) with Mockito + Scala 3 + ZIO (I think it is possible with Cats Effect as well).
I have decided to publish it because it helped me with a small project that I have been working on.
The following examples use ZIO, Try, Either and Option as Effect types.
Add the following dependencies in your sbt build, remember for now it's available only for Scala 3.
"one.estrondo" %% "sweet-mockito" % <version> % Test,
"one.estrondo" %% "sweet-mockito-zio" % <version> % Test
First, use these imports:
import one.estrondo.sweetmockito.SweetMockito
import one.estrondo.sweetmockito.zio.given // Remember, it's necessary for Scala 3.
Trait and class used by the examples:
trait Foo:
def tellMe(name: String): Task[Message]
def canIPass(name: String): IO[String, Message]
case class Message(message: String)
val mock = SweetMockito[Foo]
SweetMockito
.whenF2(mock.tellMe("Einstein")) // there is a method called whenF, but for ZIO use whenF2.
.thenReturn(Message("You were smart!"))
for result <- mock.tellMe("Einstein")
yield assertTrue(result == Message("You were smart!"))
val mock = SweetMockito[Foo]
val expectedError = IOException("You? Maybe... look at that bird!")
SweetMockito
.whenF2(mock.tellMe("Me"))
.thenFail(expectedError)
for exit <- mock.tellMe("Me").exit
yield assert(exit)(Assertion.fails(Assertion.equalTo(expectedError)))
In this case, you want to run some code to answer who is calling the mocked method, for this, you have to inform SweetMockit what kind the answer will be returned using Answer.succeed
or Answer.failed
. Please import this object:
import one.estrondo.sweetmockito.Answer
Here is an example:
val mock = SweetMockito[Foo]
val expectedError = IOException("You're a machine, not a man!")
SweetMockito
.whenF2(mock.tellMe(ArgumentMatchers.any())) // As you can see, here I'm using ArgumentMatchers from Mockito.
.thenAnswer { invocation => // Here invocation is an InvocationOnMock from Mockito too.
invocation.getArgument[String](0) match
case "Morpheus" => Answer.succeed(Message("A good leader"))
case "Agent Smith" => Answer.failed(expectedError)
case value => Answer.succeed(Message(s"Who are you $value?"))
}
for
morpheus <- mock.tellMe("Morpheus").exit
agentSmith <- mock.tellMe("Agent Smith").exit
neo <- mock.tellMe("Neo").exit
yield TestResult.all(
assert(morpheus)(Assertion.succeeds(Assertion.equalTo(Message("A good leader")))),
assert(agentSmith)(Assertion.fails(Assertion.equalTo(expectedError))),
assert(neo)(Assertion.succeeds(Assertion.equalTo(Message("Who are you Neo?"))))
)
It is quite similar, but do need to import this:
import one.estrondo.sweetmockito.zio.SweetMockitoLayer
for
_ <- SweetMockitoLayer[Foo]
.whenF2(_.canIPass("Michael Jackson")) // Remember, use whenF2 for ZIO.
.thenReturn(Message("Yes! Please take this money."))
mock <- ZIO.service[Foo]
result <- mock.canIPass("Michael Jackson")
yield assertTrue(result == Message("Yes! Please take this money."))
for
_ <- SweetMockitoLayer[Foo]
.whenF2(_.canIPass("This is my friend"))
.thenFail("I don't think so, I've never seen him!")
mock <- ZIO.service[Foo]
exit <- mock.canIPass("This is my friend").exit
yield assert(exit)(Assertion.fails(Assertion.equalTo("I don't think so, I've never seen him!")))
Import:
import one.estrondo.sweetmockito.Answer
Example:
for
_ <- SweetMockitoLayer[Foo]
.whenF2(_.canIPass(ArgumentMatchers.any()))
.thenAnswer { invocation =>
invocation.getArgument[String](0) match
case "Dr. Spock" => Answer.succeed(Message("Long live and prosper, please go ahead."))
case "Darth Vader" => Answer.failed("Nooooooo!")
case other => Answer.failed(s"$other, you're not Spock!")
}
mock <- ZIO.service[Foo]
spock <- mock.canIPass("Dr. Spock").exit
vader <- mock.canIPass("Darth Vader").exit
data <- mock.canIPass("Data").exit
yield TestResult.all(
assert(spock)(Assertion.succeeds(Assertion.equalTo(Message("Long live and prosper, please go ahead.")))),
assert(vader)(Assertion.fails(Assertion.equalTo("Nooooooo!"))),
assert(data)(Assertion.fails(Assertion.equalTo("Data, you're not Spock!")))
)
Imports that you need:
import one.estrondo.sweetmockito.SweetMockito
import one.estrondo.sweetmockito.Answer // Just if you need .thenAnswer method, there are examples bellow.
These examples came from my testing codes and I'm using ScalaTest.
For scala.util.Try (maybe in coming support for Cats Effect) you have to call whenF
instead whenF2
used by Either
and ZIO mocking.
Success example:
val mock = SweetMockito[Foo]
SweetMockito
.whenF(mock.returnAsTry(10))
.thenReturn("Ok!")
assert(mock.returnAsTry(10) == Success("Ok!"))
Failure example:
val mock = SweetMockito[Foo]
val exception = IllegalArgumentException("I'm marcian!")
SweetMockito
.whenF(mock.failAsTry("earth"))
.thenFail(exception)
assert(mock.failAsTry("earth") == Failure(exception))
Right example:
val mock = SweetMockito[Foo]
SweetMockito
.whenF2(mock.returnAsEither("Earth"))
.thenReturn(Message("Earth is OK!"))
assert(mock.returnAsEither("Earth") == Right(Message("Earth is OK!")))
Left example:
val mock = SweetMockito[Foo]
SweetMockito
.whenF2(mock.failAsEither("42"))
.thenFail("Ouch!")
assert(mock.failAsEither("42") == Left("Ouch!"))
Some example:
val mock = SweetMockito[Foo]
SweetMockito
.whenF(mock.returnAsOption("Earth"))
.thenReturn(Message("Earth is OK!"))
assert(mock.returnAsOption("Earth") == Some(Message("Earth is OK!")))
None example, for this you can use the method thenEmpty
which is available for some types.
val mock = SweetMockito[Foo]
SweetMockito
.whenF(mock.returnAsOption("Pluto"))
.thenEmpty()
assert(mock.returnAsOption("Pluto") == None)
Here I tested only scala.collection.immutable.Vector
. SweetMockito looks for a given
(implicit) scala.collection.Factory
for the type that you want to return in your mock. So, any type that has this Factory available SweetMockito could be used for it.
One element:
val mock = SweetMockito[Foo]
SweetMockito
.whenF(mock.returnAsVector("Earth"))
.thenReturn(Message("Earth is OK!"))
assert(mock.returnAsVector("Earth") == Vector(Message("Earth is OK!")))
Multiple elements. I'm sorry! I forgot! That will be available soon.
Empty vector, like Option you can return an empty Iterable
.
val mock = SweetMockito[Foo]
SweetMockito
.whenF(mock.returnAsVector("Pluto"))
.thenEmpty()
assert(mock.returnAsVector("Pluto") == Nil)
If you have any suggestions or more ideas for features, please contact me here in github.