How do I mock a property on a Scala companion object using Mockito and ScalaTest? - scala

I have a simple companion Object that is creating a connection pool that should be globally static.
object CoreDataSource {
val pool : DataSource = getDataSource
def getDataSource: DataSource = {
...
}
}
I then use that in a variety of classes like...
class Query {
var source: DataSource = CoreDataSource.pool
def getSalutations: String = {
val conn : Connection = source.getConnection
...
}
}
Now I would like to test the getSalutations without the getDataSource actually getting called. Is there a way to mock out this call so that when the initial getDataSource call happens it hits a mock instead of the actual function?
Here is the skeleton of my test suite...
#RunWith(classOf[JUnitRunner])
class MySuite extends AnyFunSuite with MockitoSugar {
test("Test mocking the Datasource") {
// Mock object here
}
}
I Also tried...
var pool : Option[DataSource] = None
def getConnection: Connection = {
if(pool.isEmpty)
pool = getDataSource
pool.get.getConnection
}
def getDataSource: Option[DataSource] = {
...
}
}
#RunWith(classOf[JUnitRunner])
class MySuite extends AnyFunSuite with MockitoSugar with Matchers {
test("Test mocking the Datasource") {
withObjectMocked[CoreDataSource.type]{
val source = mock[DataSource]
when(CoreDataSource.getDataSource).thenReturn(Some(source))
...
assert(result == "")
}
}
}
But this throws
Mockito cannot mock/spy because :
- final class

I am not going to accept my workaround because it is ugly but this ended up working...
class CoreDataSource {
def getConnection = {
CoreDataSource.getConnection
}
}
object CoreDataSource {
var pool : Option[DataSource] = None
def getConnection: Connection = {
if(pool.isEmpty)
pool = getDataSource
pool.get.getConnection
}
def getDataSource: Option[DataSource] = {
...
}
}
class Query(ds: CoreDataSource = new CoreDataSource) {
def getSalutations: String = {
...
}
}
Then the following will work...
test("Single record returned") {
val cds = mock[CoreDataSource]
val source = mock[Connection]
doNothing.when(source).close()
when(cds.getConnection).thenReturn(source)
val q = new Query(cds)
val result = q.getSalutations
assert(result == "This is the test value")
}

Related

Mocking scala object called by under another object

I am trying to write unit test for a function under object1.
object Object1 {
def main(sysArgs: Array[String]): Unit = {
val inputDF: DataFrame = UtilObject.getInput()
}
}
object UtilObject {
def getInput(){
...
}
}
To write Unit test, I am using MockitoSugar.
"object1Main" should "should make correct calls" in {
val inputArgs = Array("abc")
val util = mock[UtilObject.type]
when(util.getInput().thenReturn(inputData))
Object1.main(inputArgs)
}
While running the test, it doesn't consider the util mock and just execute the getInput() function.
I think I am missing some sort of injection here. Any ideas?
Thanks in advance!
Mocking Scala objects should be impossible conceptually speaking. An object in Scala is a pure singleton. That means there can only be one member of that type at any time.
mockito-scala can mock Scala objects via reflection. I'll use a result type of String, instead of a DataFrame, but the idea is the same:
object UtilObject {
def getInput(): String = {
// ...
"done"
}
}
object Object1 {
def main(sysArgs: Array[String]): String = {
val inputDF: String = UtilObject.getInput()
inputDF
}
}
// in test file:
"object1Main" should {
"should make correct calls" in {
val inputArgs = Array("abc")
withObjectMocked[UtilObject.type] {
UtilObject.getInput() returns "mocked!"
Object1.main(inputArgs) shouldBe "mocked!"
}
Object1.main(inputArgs) shouldBe "done"
}
}
This mocks the singleton's method only inside the block of withObjectMocked.
Usually such powerful techniques often tend to be overused or misused, so I don't generally recommend them, unless the design cannot be refactored.
Luckily, yours can: the easiest way is to use Dependency Injection with a class or a function. For DI with a class you require to convert the object being mocked into a class:
class UtilObject {
def getInput(): String = {
// ...
"done"
}
}
object Object1 {
def main(sysArgs: Array[String], ut: UtilObject): String = {
val inputDF: String = ut.getInput()
inputDF
}
}
// in test file:
"object1Main" should {
"should make correct calls" in {
val inputArgs = Array("abc")
val util = mock[UtilObject]
when(util.getInput()).thenReturn("mocked!")
Object1.main(inputArgs, util) shouldBe "mocked!"
}
}
For DI with a function you need to lift the method you want to mock into a function:
object UtilObject {
def getInput(): String = {
// ...
"done"
}
}
object Object1 {
def main(sysArgs: Array[String], f: () => String = UtilObject.getInput): String = {
val inputDF: String = f()
inputDF
}
}
// in test file:
"object1Main" should {
"should make correct calls" in {
val inputArgs = Array("abc")
val f = mock[() => String]
when(f()).thenReturn("mocked!")
Object1.main(inputArgs, f) shouldBe "mocked!"
}
}
Since the function takes no arguments, you can convert it into a by-name parameter. I'll leave that to you.
Lastly, another way is to create a trait with the method you want to mock and extend that with the object. But now Object1 requires being a class and have a reference to the object being mocked:
object UtilObject extends Utils {
def getInput(): String = {
// ...
"done"
}
}
trait Utils {
def getInput(): String
}
class Object1 {
val uo: Utils = UtilObject
def main(sysArgs: Array[String]): String = {
val inputDF: String = uo.getInput()
inputDF
}
}
// in test file:
"object1Main" should {
"should make correct calls" in {
val classUnderTest = new Object1 {
override val uo = mock[Utils]
}
val inputArgs = Array("abc")
when(classUnderTest.uo.getInput()).thenReturn("mocked!")
classUnderTest.main(inputArgs) shouldBe "mocked!"
}
}
As you can see, there are a few ways to go. Neither is inherently wrong. It mostly depends on your requirements(e.g. you can't afford adding a dependency just for one UT), needs(e.g. does the object I am testing really needs to be an object or can I make it a class?), guidelines (e.g. your team decided to avoid using powerful testing frameworks that rely on reflection and use DI as much as possible instead) and personal preferences.

Scala Testing with Mock objects within objects

While Testing Called1.writeData and mocking using the traits, control is still going to the original implementation. Need help from experts.
object Caller1() {
def method1(param1:String, param2:Long) {
val param3=param1 //Some transformation
val param4=param2
Called1.writeData(param3, param4)
}
}
object Called1 extends Called1Trait {
val producer = Called2.createProducer
def writeData {
val data = createData(param1)
Called2.MethodA(...)
}
def createData(param1) {
// Some code
}
}
object Called2 extends Called2Trait {
def createProducer {
// Some code
}
def methodA {
// Some code
}
def methodB {
// methodA is used here
}
}
I have used mocking of the traits and using when and then to bypass the execution. Yet it is going to the methods. I have used any().
val CalledObject1Mock = mock[CalledObject1Trait]
val CalledObject2Mock = mock[CalledObject2Trait]
when(CalledObject1Mock.createProducer).thenReturn(ProducerMock)
when(CalledObject1Mock.writeData(any(),any())).thenReturn(true)
when(CalledObject1Mock.createData(any())).thenReturn(MockedData)
when(CalledObject2Mock.methodA(any(), any(), any())).thenReturn(true)
when(CalledObject2Mock.methodB(any(), any(), any())).thenReturn(true)
Called1.writeData(testParam1, testParam2) // this is failing
We have used scala Mockito. So do not intend to use any other framework.
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.BeforeAndAfterAll
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatestplus.mockito.MockitoSugar
import org.scalatest.matchers.should._
trait BaseTestSuite extends AnyFunSuite with BeforeAndAfterAll with Matchers with MockitoSugar {
The current design does not allow to test it by Mock.
You mocked the called objects but don't use them. The original Called1 does not know about mocked objects.
You should not mock a class under the testing.
To fix it I would suggest redesigning your classes this way:
class Caller1(called1: Called1Trait) {
def method1(param1:String, param2:Long) {
val param3=param1 //Some transformation
val param4=param2
called1.writeData(param3, param4)
}
}
class Called1(called2: Called2Trait) extends Called1Trait {
val producer = called2.createProducer
def writeData {
val data = createData(param1)
called2.methodA(...)
}
def createData(param1) {
// Some code
}
}
class Called2 extends Called2Trait {
def createProducer {
// Some code
}
def methodA {
// Some code
}
def methodB {
// methodA is used here
}
}
So, assume you want to test the Called1. The test will look like this:
val called2Mock = mock[Called2Trait]
when(called2Mock.methodA(any(), any(), any())).thenReturn(true)
when(called2Mock.methodB(any(), any(), any())).thenReturn(true)
val called1 = new Called1(called2Mock)
called1.writeData(testParam1, testParam2)
Now the called1 knows that called2 is mocked object and will call the mocked methodA().

Can't get Guice Injector Instance play 2.5 scalatest

We are using scalatestplus-play 1.5.1 version.
Here is the code snippet for BaseTestClass :
trait GuiceMock extends Suite with OneAppPerTest with BeforeAndAfterAll {
def injector(): Injector = { app.injector }
override def newAppForTest(testData: TestData): Application = {
new GuiceApplicationBuilder().configure().configure(configurationMap)
.disable(classOf[SomeModule], classOf[RedisSentinelModule])
.bindings(getTestModule())
.in(Mode.Test).build()
}
...
}
In my test I need an instance of injector similar to below :
trait ControllerFixture extends MockitoSugar with GuiceMock {
...
val someClient = injector.instanceOf(classOf[SomeClient])
...
}
But I am met with a null pointer exception # this line :
def injector(): Injector = { app.injector }
Any pointers ?

Play framework, configuration parameter injection in my custom class.

Is there another way than using a subclass of the Controller to benefit from the injected Play Framework pooled database parameter db ?
This is what the Documentation recommends :
class ScalaControllerInject #Inject()(db: Database) extends Controller {
def index = Action {
var outString = "Number is "
val conn = db.getConnection()
...
Ok(outString)
}
}
However I would like to have another class querying the database. This is because I am not using a subclass of the Controller class : I am using a websocket Actor.
This something I would like to aim for:
object MyWebSocketActor {
def props(out: ActorRef) = Props(new MyWebSocketActor(out))
}
class MyWebSocketActor(out: ActorRef) extends Actor {
val gson = ToolSingleton.getInstance().getGsonTool()
def receive = {
case msg: String => {
val dbm=(new DbManager(... )). query(msg)
}
}
}
class DbManager #Inject()(db: Database) extends /* ? */ {
def query(s : String) {
var outString = "Number is "
val conn = db.getConnection()
...
}
}
How should I extend DbManager in order to use the function query from my actor MyWebSocketActor?

Scala closure context

I am not a Groovy expert, but I did read the book "Groovy in Action". In Groovy, each closure comes with a "context", where the items inside the closure can get access to pseudo-variables like "this", "owner", and "delegate", that let the items know who called the closure. This allows one to write DSLs like this (from Groovy in Action):
swing = new SwingBuilder()
frame = swing.frame(title:'Demo') {
menuBar {
menu('File') {
menuItem 'New'
menuItem 'Open'
}
}
panel {
// ...
}
}
Note that 'menuBar' "knows" that it belongs to 'frame' because it can get context information about the owner and delegate of the closure.
Is this possible to do in Scala? If so, how?
One way is to use a scala.util.DynamicVariable to track the context. Something like the SwingBuilder could be implemented as
import scala.util.DynamicVariable
import javax.swing._
object SwingBuilder {
case class Context(frame: Option[JFrame], parent: Option[JComponent])
}
class SwingBuilder {
import SwingBuilder._
val context = new DynamicVariable[Context](Context(None,None))
def frame(title: String)(f: =>Unit) = {
val res = new JFrame(title)
res.add(new JPanel())
context.withValue(Context(Some(res),context.value.parent)){f;res}
}
def menuBar(f: =>Unit) = {
val mb = new JMenuBar()
context.value.frame.foreach(_.setJMenuBar(mb))
context.withValue(Context(context.value.frame,Some(mb))){f;mb}
}
def menu(title: String)(f: =>Unit) = {
val m = new JMenu(title)
context.value.parent.foreach(_.asInstanceOf[JMenuBar].add(m))
context.withValue(Context(context.value.frame,Some(m))){f;m}
}
def menuItem(title: String) = {
val mi = new JMenuItem(title)
context.value.parent.foreach(_.asInstanceOf[JMenu].add(mi))
}
}
object Test {
def main(args: Array[String]) {
val builder = new SwingBuilder()
import builder._
val f = frame("Demo") {
val mb = menuBar {
menu("File") {
menuItem("New")
menuItem("Open")
}
}
}
f.setVisible(true)
}
}