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

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.

Related

Dependency mocking in scala

class Service1 #Inject()(service2: Service2) {
val url = service2.REDIS_URL
}
class TestService #Inject()(service1: Service1) {
def foo() => {}
}
I have the above 2 classes.
I need to test TestService.foo. Following is the code that I am trying but its not working.
class TestServiceTest extends org.scalatest.AsyncFunSuite with MockFactory {
val service1Mock = mock[Service1]
....
....
}
While initiating the test cases service2.REDIS_URL fails with null pointer error.
I am unable to find any answers in the scala mock documentation about how to properly mock services/singleton objects.
Update:
class Service2 #Inject()(){
val REDIS_URL = "some constant"
}
class Service1 #Inject()(service2: Service2){
val redisStr = service2.REDIS_URL
def getUrl = redisStr
}
class TestService #Inject()(service1: Service1){
def foo() = service1.getUrl
}
it should "test properly" in {
val mocks1 = mock[Service1]
}
This is not working
but if we change Service1 to
class Service1 #Inject()()(service2: Service2) {
def url = service2.REDIS_URL
}
it works.
But,
class Service1 #Inject()()(service2: Service2) {
def url = service2.REDIS_URL
config.useSingleServer()
.setAddress(REDIS_URL)
}
Again fails
This is due to service2 being null while the Mock is generated. This is very weird that the class is run while creating the Mock in ScalaTest and it finds service2 to be null causing NPE.
No, you cannot mock singleton objects in Scala. But I don't see any in your code. And you mock services just like any other class in Scala.
I am not sure I understand what your actual problem is, but I will try explain the best I can what I understood so far. As someone already said you have to tell your mock what calls to mock, otherwise of course it has no choice but to return null to whatever tries dereferencing it.
By mixing in MockFactory this means you are using the mock method of ScalaMock. A known limitation of ScalaMock is that it does not support mocking of val fields. This is because mocks are generated using macros as anonymous subclasses of the class to mock. But the Scala compiler does not allow overriding of val fields in subclasses, because val fields are immutable.
So there is no way you can mock service1.url, as long as url remains a val. A quick fix is converting the url into a def, so you can then mock the call to the method url and that should solve your null pointer issue. Here's that idea in action:
class Service1 #Inject() (service2: Service2) {
def url: String = service2.REDIS_URL
}
class TestService #Inject() (service1: Service1) {
def foo(): String = "this is " + service1.url
}
// ...
import org.scalamock.scalatest.MockFactory
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
class ProgramTest extends AnyWordSpec with Matchers with MockFactory {
"mock Service1 url " in {
val service1Mock = mock[Service1]
val mytestService = new TestService(service1Mock)
(service1Mock.url _).expects().returns("somethingelse")
val res = mytestService.foo()
res shouldBe "this is somethingelse" // true
}
}
This works. No nulls here. If for some reason, you don't want to change the url into a def, a better alternative is to switch to mockito-scala because that can mock fields as well. You don't need ScalaMock for this.
If I understood correctly and your mock of Service1 is still failing with ScalaMock even after changing url to def for some unknown reason, then that's one more reason to switch to mockito-scala. I could not reproduce your null pointer using it. First import this:
libraryDependencies += "org.mockito" %% "mockito-scala" % "1.17.7" % Test
I tested TestService.foo as follows:
class Service1 #Inject() (service2: Service2) {
val url: String = service2.REDIS_URL
}
class TestService #Inject() (service1: Service1) {
def foo(): String = "this is " + service1.url
}
// ...
import org.mockito.MockitoSugar
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
class ProgramTest extends AnyWordSpec with MockitoSugar with Matchers {
"mock Service1 url " in {
val service1Mock = mock[Service1]
val mytestService = new TestService(service1Mock)
when(service1Mock.url).thenReturn("somethingelse")
val res = mytestService.foo()
res shouldBe "this is somethingelse" // true
}
}
And it worked as expected.

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/

Check what the method of mocked object receives

In my project, whenever a class produces some output, instead of doing println it calls OutputStore.write, which is a class and method I defined.
I am trying to test the output of another class so I mocked OutputStore. I want to see what parameters it receives to OutputStore.write.
val mockOutputStore = mock[OutputStore]
I would like to do something like this:
val argument = ArgumentCaptor.forClass(classOf[OutputStore])
verify(mockOutputStore).write(argument.capture())
assertEquals("some parameter", argument.getValue())
However, this doesn't compile as verify is not even recognized.
The signature of my test class is this:
class SomeUnitTestSet extends org.scalatest.FunSuite with MockitoSugar with PropertyChecks
Any idea how to check what parameters a mocked object's method receives?
Here is a translation of what #JBNizet suggested into a Scala code
Assuming you have your OutputStore class
class OutputStore {
def write(msg: String) = {
println(msg)
}
}
and some OutputStoreApiUser class
class OutputStoreApiUser(val outputStore: OutputStore) {
def foo(): Unit = {
outputStore.write("some parameter")
outputStore.write("some parameter2")
}
}
Then your test might be something like this (in real life you probably #Inject outputStore but this is not relevant here):
import org.mockito.Mockito.verify // static import!
import org.scalatest.mockito.MockitoSugar
import org.scalatest.prop.PropertyChecks
class SomeUnitTestSet extends org.scalatest.FunSuite with MockitoSugar with PropertyChecks {
test("Capture calls"){
val mockOutputStore = mock[OutputStore]
val apiUser = new OutputStoreApiUser(mockOutputStore)
apiUser.foo()
verify(mockOutputStore).write("some parameter")
verify(mockOutputStore).write("some parameter2")
}
}
This one compiles and works for me as I would expect

How to mock using external call in Akka Actor using ScalaTest

I am new to entire ecosystem including Scala, Akka and ScalaTest
I am working on a problem where my Actor gives call to external system.
case object LogProcessRequest
class LProcessor extends Actor {
val log = Logging(context.system, this)
def receive = {
case LogProcessRequest =>
log.debug("starting log processing")
LogReaderDisruptor main(Array())
}
}
The LogReaderDisruptor main(Array()) is a Java class that does many other things.
The test I have currently looks like
class LProcessorSpec extends UnitTestSpec("testSystem") {
"A mocked log processor" should {
"be called" in {
val logProcessorActor = system.actorOf(Props[LProcessor])
logProcessorActor ! LogProcessRequest
}
}
}
where UnitTestSpec looks like (and inspired from here)
import akka.actor.ActorSystem
import akka.testkit.{ImplicitSender, TestKit}
import org.scalatest.matchers.MustMatchers
import org.scalatest.{BeforeAndAfterAll, WordSpecLike}
abstract class UnitTestSpec(name: String)
extends TestKit(ActorSystem(name))
with WordSpecLike
with MustMatchers
with BeforeAndAfterAll
with ImplicitSender {
override def afterAll() {
system.shutdown()
}
}
Question
How can I mock the call to LogReaderDisruptor main(Array()) and verify that it was called?
I am coming from Java, JUnit, Mockito land and something that I would have done here would be
doNothing().when(logReaderDisruptor).main(Matchers.<String>anyVararg())
verify(logReaderDisruptor, times(1)).main(Matchers.<String>anyVararg())
I am not sure how to translate that with ScalaTest here.
Also, This code may not be idiomatic, since I am very new and learning
There are a few ways to do this. The kind of OO way is to wrap logDisrupter as an object and pass it in. I would set up a companion object to instantiate the actor. Something like below. Then you can pass alternate implementation. You can also achieve a similar approach by using traits and mixing in an alternative logDisrupter only as needed.
object LProcessor {
def props(logDisrupter : LogDisrupter) = Props(new LProcessor(logDisrupter))
}
class LProcessor(logDisrupter : LogDisrupter) extends Actor {
val log = Logging(context.system, this)
def receive = {
case LogProcessRequest =>
log.debug("starting log processing")
logDisrupter.doSomething();
}
}
Then instatiate as
val logProcessorActor = system.actorOf(LProcessor.props(logDisrupter))

Why is this specs2 test using Mockito passing?

Suppose I had this interface and class:
abstract class SomeInterface{
def doSomething : Unit
}
class ClassBeingTested(interface : SomeInterface){
def doSomethingWithInterface : Unit = {
Unit
}
}
Note that the doSomethingWithInterface method does not actually do anything with the interface.
I create a test for it like this:
import org.specs2.mutable._
import org.specs2.mock._
import org.mockito.Matchers
import org.specs2.specification.Scope
trait TestEnvironment extends Scope with Mockito{
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
}
class ClassBeingTestedSpec extends Specification{
"The ClassBeingTested" should {
"#doSomethingWithInterface" in {
"calls the doSomething method of the given interface" in new TestEnvironment {
test.doSomethingWithInterface
there was one(interface).doSomething
}
}
}
}
This test passes. Why? Am I setting it up wrong?
When I get rid of the scope:
class ClassBeingTestedSpec extends Specification with Mockito{
"The ClassBeingTested" should {
"#doSomethingWithInterface" in {
"calls the doSomething method of the given interface" in {
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
test.doSomethingWithInterface
there was one(interface).doSomething
}
}
}
}
The test fails as expected:
[info] x calls the doSomething method of the given interface
[error] The mock was not called as expected:
[error] Wanted but not invoked:
[error] someInterface.doSomething();
What is the difference between these two tests? Why does the first one pass when it should fail? Is this not an intended use of Scopes?
When you mix-in the Mockito trait to another trait you can create expectations like there was one(interface).doSomething. If such an expression fails it only returns a Result, it doesn't throw an Exception. It then gets lost in a Scope because it is just a "pure" value inside the body of a trait.
However if you mix-in the Mockito trait to a mutable.Specification then an exception will be thrown on a failure. This is because the mutable.Specification class specifies that there should be ThrownExpectations by mixing in that trait.
So if you want to create a trait extending both Scope you can either:
create the trait from inside the specification and not have it extend Mockito:
class MySpec extends mutable.Specification with Mockito {
trait TestEnvironment extends Scope {
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
}
...
}
create trait and specification as you do, but mix-in org.specs2.execute.ThrownExpectations
trait TestEnvironment extends Scope with Mockito with ThrownExpectations {
val interface = mock[SomeInterface]
val test = new ClassBeingTested(interface)
}
class MySpec extends mutable.Specification with Mockito {
...
}