How to mock method inside method in scala object? - scala

I would like to use a simple example for my problem.
I have a below trait:
trait ReaderTrait {
def readTable(tableName:String, closingDate: String)(implicit sparkSession: SparkSession): DataFrame = {
sparkSession.read.table(
tableName
).filter(col("closingDate") === closingDate)
}
def read(fullTable:String, closingDate: String)(implicit sparkSession: SparkSession): DataFrame
}
I have below Object:
object Reader extends ReaderTrait
{
override final def read(fullTable: String, closingDate: String)(implicit sparkSession: SparkSession): DataFrame =
{
readTable(fullTable: String, closingDate: String)
//.filter..
//I have business logic here which I want to test, I want to mock only readTable
}
}
Now I want to mock only the readTable method in Object.
I have a Test class:
class ReaderTest extends FeatureSpec with MockitoSugar {
scenario("first") {
//sample dataframe1 created ......
val mockTrait = mock[Reader.type ]
when(mockTrait.readTable("abc", "sss")(spark)) thenReturn (dataframe1)
val result = mockTrait.read("abc", "sss")(spark)
result.show()
}
}
After running the above test class, it's giving me the below error:
java.lang.NullPointerException was thrown.
Please help me to resolve this situation; I have to mock the readTable method in read method in object without making any changes to existing code.

Many problems here:
You cannot mock objects.
mock[Reader.type] does not make sense. You could mock the trait with mock[ReaderTrait] though.
Mock doesn't work the way you seem to think.
If you have class A { def foo = ""; def bar = foo }, and then you do
val m = mock[A]
when(m.foo).thenReturn("baz")
val result = m.bar
the value of result will not be "baz" it will be null. Why? Because m is a mock – a dummy object, with all of the methods stubbed out. m.bar does not call foo, because it is a stub, it just returns null.
There is a way to stub only some methods of a class, and have others keep original behavior. If you'd like to learn more about it, look up the concept of a spy (you would do spy(new A) instead of mock[A]). I will not get into this here, because it is not a good practice to use spies. Which brings me to my third point ...
You should not stub methods of the class you are testing. When you need to, it is a symptom of your class design violating the single responsibility principle, which is not a good thing.
The proper way of writing your test in this case would be isolate the readTable "provider" class from the functionality you are testing.
For example:
trait TableReader {
def readTable(table:String, date: String)(implicit s: SparkSession) =
s.read.table(table).filter(col("closingDate") === date)
}
object TableReader extends TableReader
class Reader(val tr: TableReader) {
def read(table: String, date: String)(implicit s: SparkSession) =
tr.readTable(table, date)
}
object Reader extends Reader(TableReader)
Now you can test your Reader class with something like:
val tr = mock[TableReader]
wen(tr.readTable(any, any)(any)).thenReturn(dataframe1)
val reader = new Reader(tr)
reader.read("foo", "bar")(spark) shouldBe dataframe1
verify(tr).readTable("foo", "bar")(spark)
This will work, but kinda unclear what it is you are trying to test here.
You are not testing readTable because you stubbed it out. And there is no logic in read, so nothing to test there.
I would suggest to move the filtering logic out from TableReader into Reader, that would make for a cleaner separation of responsibilities between the two classes: one is responsible for fetching data from the table, and the other applies filtering and other business logic:
trait TableReader {
def readTable(t: String)(implicit s: SparkSession) = s.read.table(t)
}
class Reader(tr: TableReader) {
def read(table: String, date: String)(implicit s: SparkSession) =
tr.readTable(table).filter(col("closingDate") === date)
}
This gives you an ability to write meaningful tests, like:
reader.read("foo", "bar")(spark)
.select("closingDate")
.as[String]
.collect
.toList shouldBe List("bar", "bar", "bar")
(basically, checking that the filter for closingDate === "bar" was properly applied to the dataframe)
The idea here is that you do not need to test TableReader, as there is no any business logic there, it is only a simple wrapper around spark, so you can stub it out easily.
And the Reader class is only concerned about business logic that you do want to test, and does not know anything about where the actual data comes from, so you don't need to stub any of its methods, just supply a mock provider of the data (mock[TableReader]).

Related

How to test a method by mocking nested methods?

I'm trying to test an Object.method which contains some nested methods from a Trait apart of some calculations. These nested methods have to be mocked (they access to a DB so I want to mock their responses).
When I call the real Object.method, it should skip the nested methods call and retrieve what I want. I've tried mocking them but test is still calling them.
Here's my example source code:
trait MyTrait {
def myMethodToMock(a: String): String
}
object MyObject extends MyTrait {
def myParentMethod(a:String) = {
val b = myMethodToMock(a)
val c = a + b
c
}
}
Then in my test:
val myTraitMock = mock[MyTrait]
when(myTraitMock.myMethodToMock(a)).thenReturn(b)
//Then I call the parent method:
assert(MyObject.myParentMethod(a) equals c)
It throws a NullPointerException as it's still accessing to myMethodToMock
Your code does not compile, so I am going to guess some things of what you are actually trying to do here ...
You are stubbing a method on a mock, and then calling it on a completely unrelated instance. No wonder it does not work.
A good rule of thumb (and the best practice) is to never mock classes you are actually testing. Split everything you want to mock and test separately into a separate class. This is also known as single responsibility principle (each component should be responsible for a single thing).
trait MyTrait {
def myMethodToMock(a: String): String
}
object MyTrait extends MyTrait {
def myMethodtoMock(a: String) = ???
}
class MyObject(helper: MyTrait = MyTrait) {
def myParentMethod(a: String) = a + helper.myMethodToMock(a)
}
object MyObject extends MyObject()
Now, you can write your test like this:
val myTraitMock = mock[MyTrait]
when(myTraitMock.myMethodToMock(any)).thenReturn("b")
new MyObject(myTraitMock).myParentMethod("a") shouldBe "ab"
verify(myTraitMock).myMethodToMock("a")
The main difference here is that you are passing your mock into the object's constructor, so that when it calls the method, it will be the one you stubbed, not the implementation provided by the default class.
You should use composition rather than inheritance, so you can inject an instance of MyTrait that can be a mock or the real one

Way to enhance a class with function delegation

I have the following classes in Scala:
class A {
def doSomething() = ???
def doOtherThing() = ???
}
class B {
val a: A
// need to enhance the class with both two functions doSomething() and doOtherThing() that delegates to A
// def doSomething() = a.toDomething()
// def doOtherThing() = a.doOtherThing()
}
I need a way to enhance at compile time class B with the same function signatures as A that simply delegate to A when invoked on B.
Is there a nice way to do this in Scala?
Thank you.
In Dotty (and in future Scala 3), it's now available simply as
class B {
val a: A
export a
}
Or export a.{doSomething, doOtherThing}.
For Scala 2, there is unfortunately no built-in solution. As Tim says, you can make one, but you need to decide how much effort you are willing to spend and what exactly to support.
You can avoid repeating the function signatures by making an alias for each function:
val doSomething = a.doSomething _
val doOtherthing = a.doOtherThing _
However these are now function values rather than methods, which may or may not be relevant depending on usage.
It might be possible to use a trait or a macro-based solution, but that depends on the details of why delegation is being used.
Implicit conversion could be used for delegation like so
object Hello extends App {
class A {
def doSomething() = "A.doSomething"
def doOtherThing() = "A.doOtherThing"
}
class B {
val a: A = new A
}
implicit def delegateToA(b: B): A = b.a
val b = new B
b.doSomething() // A.doSomething
}
There is this macro delegate-macro which might just be what you are looking for. Its objective is to automatically implement the delegate/proxy pattern, so in your example your class B must extend class A.
It is cross compiled against 2.11, 2.12, and 2.13. For 2.11 and 2.12 you have to use the macro paradise compile plugin to make it work. For 2.13, you need to use flag -Ymacro-annotations instead.
Use it like this:
trait Connection {
def method1(a: String): String
def method2(a: String): String
// 96 other abstract methods
def method100(a: String): String
}
#Delegate
class MyConnection(delegatee: Connection) extends Connection {
def method10(a: String): String = "Only method I want to implement manually"
}
// The source code above would be equivalent, after the macro expansion, to the code below
class MyConnection(delegatee: Connection) extends Connection {
def method1(a: String): String = delegatee.method1(a)
def method2(a: String): String = delegatee.method2(a)
def method10(a: String): String = "Only method I need to implement manually"
// 96 other methods that are proxied to the dependency delegatee
def method100(a: String): String = delegatee.method100(a)
}
It should work in most scenarios, including when type parameters and multiple argument lists are involved.
Disclaimer: I am the creator of the macro.

Typeclass or add a method via implicit?

I'm designing sort of Services and faced with a design issue. Here is what I currently have:
trait Service {
def isFailed(): Boolean
def start(): Unit
def stop(): Unit
}
And in order to group Services related to each other in a group (in order to restart/recover the group, not other services) I created the following package object:
package object app {
type FaultTolerantServiceGroup = Seq[Service]
object FaultTolerantServiceGroup{
def apply(svcs: Service*): FaultTolerantServiceGroup = Seq(svcs: _*)
}
class FaultTolerantServiceGroupOps(val F: FaultTolerantServiceGroup){
def hasFailed: Boolean = F.forall(_.failed())
}
trait FaultTolerantServiceGroupSyntax{
implicit def serviceGroup2Ops(F: FaultTolerantServiceGroup) = new FaultTolerantServiceGroupOps(F)
}
}
So I added the method hasFailed to FaultTolerantServiceGroup. But I'm not sure about this decision.
Would it be better to define a typeclass, say
trait Watchable[T]{
def hasFailed(t: T): Boolean
}
And implicitly provide an instance of Watchable[FaultTolerantServiceGroup]?
In my humble opinion implicit functions become much harder to read afterwards. Even when reading my old code it can sometimes be confusing when objects have methods that appear out of the blue.
I have yet to see an instance where implicits are easier to reason about than declarative functions:
val failedGroup : FaultTolerantServiceGroup => Boolean = _.forall(_.failed())
The resulting code doesn't seem any better, or worse, than implicits but at least it's obvious where functionality is coming from:
val group : FaultTolerantServiceGroup = ???
//no implicit
val failed = failedGroup(group)
//with implicits : how does a Seq have a hasFailed method?
val failed = group.hasFailed
Explicit functions also make Iterable functions easier to read:
val groups : Iterable[FaultTolerantServiceGroup] = ???
val failedGroups = groups filter failedGroup

Scala Type Classes Understanding Interface Syntax

I'm was reading about cats and I encountered the following code snippet which is about serializing objects to JSON!
It starts with a trait like this:
trait JsonWriter[A] {
def write(value: A): Json
}
After this, there are some instances of our domain object:
final case class Person(name: String, email: String)
object JsonWriterInstances {
implicit val stringWriter: JsonWriter[String] =
new JsonWriter[String] {
def write(value: String): Json =
JsString(value)
}
implicit val personWriter: JsonWriter[Person] =
new JsonWriter[Person] {
def write(value: Person): Json =
JsObject(Map(
"name" -> JsString(value.name),
"email" -> JsString(value.email)
))
}
// etc...
}
So far so good! I can then use this like this:
import JsonWriterInstances._
Json.toJson(Person("Dave", "dave#example.com"))
Later on I come across something called the interface syntax, which uses extension methods to extend existing types with interface methods like below:
object JsonSyntax {
implicit class JsonWriterOps[A](value: A) {
def toJson(implicit w: JsonWriter[A]): Json =
w.write(value)
}
}
This then simplifies the call to serializing a Person as:
import JsonWriterInstances._
import JsonSyntax._
Person("Dave", "dave#example.com").toJson
What I don't understand is that how is the Person boxed into JsonWriterOps such that I can directly call the toJson as though toJson was defined in the Person case class itself. I like this magic, but I fail to understand this one last step about the JsonWriterOps. So what is the idea behind this interface syntax and how does this work? Any help?
This is actually a standard Scala feature, since JsonWriterOps is marked implicit and is in scope, the compiler can apply it at compilation-time when needed.
Hence scalac will do the following transformations:
Person("Dave", "dave#example.com").toJson
new JsonWriterOps(Person("Dave", "dave#example.com")).toJson
new JsonWriterOps[Person](Person("Dave", "dave#example.com")).toJson
Side note:
It's much more efficient to implicit classes as value classes like this:
implicit class JsonWriterOps[A](value: A) extends AnyVal
This makes the compiler also optimize away the new object construction, if possible, compiling the whole implicit conversion + method call to a simple function call.

How to swap JSON Writes Converter for Play controller Action

I've built a microservice using Scala and Play and now I need to create a new version of the service that returns the same data as the previous version of the service but in a different JSON format. The service currently uses implicit Writes converters to do this. My controller looks something like this, where MyJsonWrites contains the implicit definitions.
class MyController extends Controller with MyJsonWrites {
def myAction(query: String) = Action.async {
getData(query).map {
results =>
Ok(Json.toJson(results))
}
}
}
trait MyJsonWrites {
implicit val writes1: Writes[SomeDataType]
implicit val writes2: Writes[SomeOtherDataType]
...
}
Now I need a new version of myAction where the JSON is formatted differently. The first attempt I made was to make MyController a base class and have subclasses extend it with their own trait that has the implicit values. Something like this.
class MyNewContoller extends MyController with MyNewJsonWrites
This doesn't work though because the implicit values defined on MyNewJsonWrites are not available in the methods of the super class.
It would be ideal if I could just create a new action on the controller that somehow used the converters defined in MyNewJsonWrites. Sure, I could change the trait to an object and import the implicit values in each method but then I'd have to duplicate the method body of myAction so that the implicits are in scope when I call Json.toJson. I don't want to pass them as implicit parameters to a base method because there are too many of them. I guess I could pass a method as a parameter to the base method that actually does the imports and Json.toJson call. Something like this. I just thought maybe there'd be a better way.
def myBaseAction(query: String, toJson: Seq[MyResultType] => JsValue) = Action.async {
getData(query).map {
results =>
Ok(Json.toJson(results))
}
}
def myActionV1(query: String) = {
def toJson(results: Seq[MyResultType]) = {
import MyJsonWritesV2._
Json.toJson(results)
}
myBaseAction(query, toJson)
}
Instead of relying on scala implicit resolution, you can call your writes directly:
def myBaseAction(query: String, writes: Writes[MyResultType]) = Action.async {
getData(query).map { results =>
val seqWrites: Writes[Seq[MyResultType]] = Writes.seq(writes)
Ok(seqWrites.writes(results))
}
}
def myActionV1(query: String) = myBaseAction(query, MyJsonWritesV1)
def myActionV2(query: String) = myBaseAction(query, MyJsonWritesV2)