Scala Mockito Guice and Partial Mocking .... methods get called twice - scala

I have the following code written in Scala, Guice, Mockito and ScalaTest
import javax.inject.Singleton
import com.google.inject.Inject
#Singleton
class TestPartialMock #Inject()(t1: Test1, t2: Test2) {
def test3() = "I do test3"
def workHorse() : List[String] = {
println("+++++ came inside ++++++++")
List(t1.test1(), t2.test2(), test3())
}
}
class MainModule extends ScalaModule {
override def configure() = {
bind[Test1]
bind[Test2]
bind[TestPartialMock]
}
}
and I have written unit test cases with partial mocking
class PartialMockTest extends FunSpec with Matchers {
describe("we are testing workhorse but mock test3") {
it("should return mock for test3") {
val module = new TestModule
val injector = Guice.createInjector(module)
val tpm = injector.getInstance(classOf[TestPartialMock])
val result = tpm.workHorse()
result should contain ("i do test2")
result should contain ("i do test1")
result should contain ("I am mocked")
result should not contain ("I do test3")
}
}
}
class TestModule extends AbstractModule with ScalaModule with MockitoSugar {
override def configure() = {
val module = new MainModule()
val injector = Guice.createInjector(module)
val realobject = injector.getInstance(classOf[TestPartialMock])
val x = spy(realobject)
when(x.test3()).thenReturn("I am mocked")
when(x.workHorse()).thenCallRealMethod()
bind(classOf[TestPartialMock]).toInstance(x)
}
}
My tests are successful and I can see that it mocks the right set of methods and calls the actual implementation of the right set of methods. BUT when I look at the output I see
info] Compiling 5 Scala sources to /Users/IdeaProjects/GuicePartialMock/target/scala-2.12/classes...
[info] Compiling 1 Scala source to /Users/IdeaProjects/GuicePartialMock/target/scala-2.12/test-classes...
+++++ came inside ++++++++
+++++ came inside ++++++++
[info] PartialMockTest:
[info] we are testing workhorse but mock test3
[info] - should return mock for test3
[info] Run completed in 2 seconds, 92 milliseconds.
Why am I seeing the print statement came inside twice?
Edit::
Based on advice of Tavian ... this is the final code which worked
class TestModule extends AbstractModule with ScalaModule with MockitoSugar {
override def configure() = {
val module = new MainModule()
val injector = Guice.createInjector(module)
val realobject = injector.getInstance(classOf[TestPartialMock])
val x = spy(realobject)
when(x.test3()).thenReturn("I am mocked")
bind(classOf[TestPartialMock]).toInstance(x)
}
}

For spies, you need to be careful of expressions like
when(x.workHorse()).thenCallRealMethod()
because x.workHorse() really is invoked in this expression! x doesn't "know" that it's inside a when() call, as the expression is lowered into something like this:
tmp1 = x.workHorse();
tmp2 = when(tmp1);
tmp3 = tmp2.thenCallRealMethod();
Instead, you can write
doCallRealMethod().when(x).workHorse()
which will suppress the invocation of the real workHorse() implementation.
But, you don't need to do any of this for this example—calling real methods is the default behaviour of spies.

As you mentioned in the question title, you have a combination of 3 technologies. Actually 4 technologies including build tool that is used to run the test. You can isolate the problem by
1) Remove Guice and instantiate everything directly
2) Run code as a simple App instead of running as a test by sbt/gradle/maven.
Also it makes sense to print stack trace together with a came inside message to find a caller.

Related

Testing a Scala Aplication with ScalaTest

I have been going through documentations on ScalaTest but able to figure out what type of approach i should take for testing the app.
Code is divided amoung controller and service.
eg. Controller Code example
#Singleton
class Controller1 #Inject()(service1: ServiceClass1, authAction : AuthAction)
extends InjectedController {
//returns a list[]
def getSomeValue() = authAction {
val res = service1.getValue1()
val json = Json.toJson(res)
Ok(json)
}
}
Service Code Example -:
def getValue1() = {
implicit val graph = db.g
val infos = graph.V.hasLabel[someModel].toList()
infos.map(vertex => {
val someModel = vertex.toCC[someModel]
val item = info(someId =
someModel.someId.getOrElse("").toString,
category = SomeModel.category,
description = someModel.description)
item
})
}
I am very new to Testing and Scala both, I also understand the code but not able to understand where to begin.
This is just a sample code which is very similar.
It seems like what you're looking for is a way to mock service1.getValue1() in your Controller1.
Scalatest supports a couple different ways to do this: http://www.scalatest.org/user_guide/testing_with_mock_objects
In your case, to test def getSomeValue(); you'd need to define a mock and set the right expectations so that when called from the test, the mock returns the expected responses.
If you'd like to use scala mock, you'd need to add it as a dependency in your sbt build config. You can do that by adding this dependency to your tests:
"org.scalamock" %% "scalamock" % "4.4.0" % Test
And then, your test could be something like this:
import org.scalamock.scalatest.MockFactory
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class Controller1Spec extends AnyFlatSpec with Matchers with MockFactory {
"Controller1" should "respond with a valid response" in {
val mockService = mock[ServiceClass1]
(mockService.getValue1 _).when().returning("Some Response").once()
val mockAuthAction = mock[AuthAction] //assuming you've got an action called AuthAction
//you'd need to mock this one too, in order for it to work
(mockAuthAction.invokeBlock _) expects(_) onCall((r,b) => b(r))
new Controller1(mockService, mockAuthAction) shouldBe Ok("Some Response")
}
}
There's a number of posts on mocking for Scala which you should be able to find, like this one here: https://scalamock.org/user-guide/advanced_topics/

Mock new object creation in Scala

I want to write unit test for below scala class.
In the below implementation, QueryConfig is final case class.
class RampGenerator {
def createProfile(queryConfig: QueryConfig): String = {
new BaseQuery(queryConfig).pushToService().getId
}
}
The unit test I have written is this
#RunWith(classOf[JUnitRunner])
class RampGeneratorTest extends FlatSpec with Matchers {
"createProfile" must "succeed" in {
val rampGenerator = new RampGenerator()
val queryConfig = QueryConfig("name", "account", “role")
val baseQuery = mock(classOf[BaseQuery])
val profile = mock(classOf[Profile])
when(new BaseQuery(queryConfig)).thenReturn(baseQuery)
when(baseQuery.pushToService()).thenReturn(profile)
when(profile.getId).thenReturn("1234")
val id = rampGenerator.createProfile(queryConfig)
assert(id.equals("1234"))
}
}
Currently it gives below exception, which is expected, since I don't have mocked class used in when. How do I mock the new instance creation?
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
There are two options:
Use powermockito to mock the constructor (see this question for details)
Externalize object creation
A bit more on the second option - this is actually a testing technique that helps in a variety of situations (a couple of examples: yours, creating akka actors and asserting on hierarchies) - so it might be useful to just have it in the "toolbox".
In your case it'll look something like this:
class RampGenerator(queryFactory: QueryFactory) {
def createProfile(queryConfig: QueryConfig) = queryFactory.buildQuery(queryConfig).pushToService().getId()
}
class QueryFactory() {
def buildQuery(queryConfig: QueryConfig): BaseQuery = ...
}
#RunWith(classOf[JUnitRunner])
class RampGeneratorTest extends FlatSpec with Matchers {
"createProfile" must "succeed" in {
val rampGenerator = new RampGenerator()
val queryConfig = QueryConfig("name", "account", “role")
val queryFactory = mock(classOf[QueryFactory])
val profile = mock(classOf[Profile])
val baseQuery = mock(classOf[BaseQuery])
when(queryFactory.buildQuery(queryConfig)).thenReturn(baseQuery)
when(baseQuery.pushToService()).thenReturn(profile)
when(profile.getId).thenReturn("1234")
val id = rampGenerator.createProfile(queryConfig)
assert(id.equals("1234"))
}
}
Please note query factory does not have to be a separate factory class/hierarchy of classes (and certainly does not require something as heavyweight as abstract factory pattern - although you can use it). In particular, my initial version was just using queryFactory: QueryConfig => BaseQuery function, but mockito cannot mock functions...
If you prefer to inject factory method directly (via function), Scalamock has support for mocking functions

How to mock arbitrary blocks of code in Mockito Scala?

So I have the following bit of code
class MetricsLogger {
def measure[T](name:String)(operation: => T): T = {
val startTime = System.currentTimeMillis
val result = try {
operation
} finally {
logMetric(Metric(name, System.currentTimeMillis - startTime, StandardUnit.Milliseconds))
}
result
}
}
Where log Metric is some kind of side effect (e.g. upload metric to cloudwatch).
Now I am doing this like
def measuredOp = measure("metricName") { someOperation }
Here some operation is making some network calls.
Now I have to stub measured op.
So my stub is as the follows:-
val loggingMetrics = mock[MetricsLogger] // mock is from MockitoSugar trait
and my stubbing is like
Mockito.when(loggingMetrics.measure(Matchers.anyString())(Matchers.anyObject())).thenReturn(???)
So obviously my stubbing is wrong, but I cannot figure how to stub this properly.
Mockito doesn't support that as by-name parameters it's a concept that doesn't exists in Java, however, mockito-scala does support this from version 0.4.0 (try 0.4.0 or 0.4.2, ignore 0.4.1)
I just run a quick test like this
import org.mockito.{ MockitoSugar, ArgumentMatchersSugar }
class StackOverflowTest extends WordSpec with MockitoSugar with scalatest.Matchers with ArgumentMatchersSugar {
"mock[T]" should {
"it should work with by-name params" in {
val loggingMetrics = mock[MetricsLogger]
when(loggingMetrics.measure(any)(any)).thenReturn("it worked!")
loggingMetrics.measure("test")("") shouldBe "it worked!"
}
}
}
Disclaimer: I'm a maintainer of that library although it's part of the official Mockito suite

Scala and mockito to test twilio MessageFactory.create() method

I'd like to test our Twilio integration using some simple dependency injection and mocking. It seems to me the easiest method would be to use mockito to intercept the following message create call:
val messageFactory: MessageFactory = smsProvider.getAccount().getMessageFactory()
val message: Message = messageFactory.create(params)
Ideally I'd like to stub out the create call so that I can validate that it was called, and that the parameters are correct.
I'm new to Mockito though, and trying to piece together how to use it in Scala (and if this kind of testing is even possible). Most examples I've found are in Java, etc., and don't translate well to Scala.
You can use mockito with Scala. Add mockito to your library dependencies (e.g., using SBT something like libraryDependencies += "org.mockito" % "mockito-core" % "1.8.5" % "test") and you're set.
If you're using Scalatest, there's also MockitoSugar that you can mix in as a trait or import statically, giving you the syntactic sugar mock[ClassToMock] instead of mockito's mock(classOf[ClassToMock]).
Mockito allows you to pass a mock-object as external dependency to your "unit under test" and assert that a certain method is called. In your case, the external dependency should either be messageFactory or smsProvider. However, the latter requires you to pass a stub for smsProvider to return a mock for MessageFactory.
Your code could look as follows:
import org.mockito.Mockito.verify
import org.mockito.Matchers.any
import org.scalatest.FlatSpec
import org.scalatest.mock.MockitoSugar
// Your Test
class MessageCreatorTest extends FlatSpec with MockitoSugar {
val messageCreator = new MessageCreator
"createMessage" should "create a new message" in {
val factory = mock[MessageFactory]
messageCreator.createMessage(factory)
verify(factory).create(any[List[String]])
}
}
// Your Unit Under Test
class MessageCreator {
def createMessage(messageFactory: MessageFactory): Unit ={
messageFactory.create(List("Foo", "Bar"))
}
}
// The external dependency of your Unit Under Test
trait MessageFactory {
def create(params: List[String]) = new Message("Hello")
}
// A plain value
case class Message(message: String)
However, if your unit under test depends on smsFactory, you'll notice that setting up the test becomes more of a burden:
import org.mockito.Mockito.{when, verify}
import org.mockito.Matchers.any
import org.scalatest.FlatSpec
import org.scalatest.mock.MockitoSugar
class MessageCreatorTest extends FlatSpec with MockitoSugar {
val messageCreator = new MessageCreator
"createMessage" should "create a new message" in {
val factory = mock[MessageFactory]
val account = mock[Account]
val smsProvider = mock[SmsProvider]
when(smsProvider.getAccount).thenReturn(account)
when(account.getMessageFactory).thenReturn(factory)
messageCreator.createMessage(smsProvider)
verify(factory).create(any[List[String]])
}
}
class MessageCreator {
def createMessage(smsProvider: SmsProvider): Unit = {
val messageFactory = smsProvider.getAccount.getMessageFactory
messageFactory.create(List("Foo", "Bar"))
}
}
trait MessageFactory {
def create(params: List[String]) = new Message("Hello")
}
case class Message(message: String)
trait SmsProvider {
def getAccount: Account
}
trait Account {
def getMessageFactory: MessageFactory
}
In that case however, the test tells you that you're violating the Law of Demeter.

Mockito for Objects in Scala

I'm using Scala 2.10, specs2 and Mockito. I want to mock scala.io.Source.fromURL(). The issue seems to be fromURL() is a function in io.Source's object.
val m = mock[io.Source]
m.fromURL returns io.Source.fromString("Some random string.")
It's a pretty straightforward mock in an Unit test. Why isn't it working?
Thanks!
Instead of mocking it, you could try spying it as follows:
val m = spy(io.Source)
Or you could mock it as follows:
val m = mock[io.Source.type]
But then how are you using Source in the class you are testing? If you had an example class like so:
class MyClass{
def foo = {
io.Source.doSomething //I know doSomething is not on Source, call not important
}
}
Then in order to take advantage of mocking/spying, you'd have to structure your class like so:
class MyClass{
val source = io.Source
def foo = {
source.doSomething
}
}
And then your test would have to look something like this:
val mockSource = mock[io.Source.type]
val toTest = new MyClass{
override val source = mockSource
}
In the Java world, static methods are the bane of mocking. In the Scala world, calls to objects can also be troublesome to deal with for unit tests. But if you follow the code above, you should be able to properly mock out an object based dependency in your class.
Good news! With the latest 1.16 release of mockito-scala, you can now mock scala objects.
To enable withObjectMocked feature, it is mandatory to create the file src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker containing a single line:
mock-maker-inline
Example:
object FooObject {
def simpleMethod: String = "not mocked!"
}
"mock" should {
"stub an object method" in {
FooObject.simpleMethod shouldBe "not mocked!"
withObjectMocked[FooObject.type] {
FooObject.simpleMethod returns "mocked!"
//or
when(FooObject.simpleMethod) thenReturn "mocked!"
FooObject.simpleMethod shouldBe "mocked!"
}
FooObject.simpleMethod shouldBe "not mocked!"
}
}
See: https://github.com/mockito/mockito-scala#mocking-scala-object
Years later, above answer doesn't work as others have pointed out.
And you cannot mock the scala.io.Source object.
Can I mock final / private methods or classes? This is not supported, as mocks generated with macros are implemented as subclasses of the type to mock. So private and final methods cannot be overridden. You may want to try using an adapter or façade in your code to make it testable. It is better to test against a trait/interface instead of a concrete implementation. There are libraries that support this kind of mocking, such as PowerMock. Be aware that this kind of mocking involves Bytecode manipulation, which has the risk that your test double diverges from the actual implementation.
So what I did is an work around to abstract out scala.io.Source.fromUrl() as a function argument and pass in mocked function in tests.
// original func
def aFuncThatUsesSource() = {
val source = scala.io.Source("127.0.0.1:8080/...")
val result = source.mkString
Try(source.close())
result
}
// test friendly func that accepts `scala.io.Source.fromURL` as arg
def aTestFriendlyFunc(makeApiCall: String => BufferedSource) = {
val source = makeApiCall("127.0.0.1:8080/...")
val result = source.mkString
Try(source.close())
result
}
....
// test spec
def testyMcTesterson = () => {
val makeApiCall = mockFunction[String, BufferedSource]
makeApiCall.expects("something...")
.returns( new BufferedSource(new ByteArrayInputStream("returns something".getBytes)) )
aTestFriendlyFunc(makeApiCall) shouldEqual "returns something"
}