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().
Related
I need to write two functions to get the output format and the output index for file conversion. As part of this, I wrote a TransformSettings class for these methods and set the default value. And in the transformer class, I created a new object of TransformSettings class to get the default values for each job run. Also, I have another class called ParquetTransformer that extends Transformer where I want to change these default values. So I implemented like below.
class TransformSettings{
def getOuputFormat: String = {
"orc"
}
def getOuputIndex(table: AWSGlueDDL.Table): Option[String] = {
table.StorageDescriptor.SerdeInfo.Parameters.get("orc.column.index.access")
}
}
class Transformer{
def getTransformSettings: TransformSettings = {
new TransformSettings
}
def posttransform(table: AWSGlueDDL.Table):Dateframe ={
val indexAccess = getTransformSettings.getOuputIndex(table: AWSGlueDDL.Table)
........
}
}
class ParquetTransformer extends Transformer{
override def getTransformSettings: TransformSettings = {
val transformSettings = new TransformSettings {
override def getOuputFormat: String = {
"parquet"
}
override def getOuputIndex(table: AWSGlueDDL.Table): Option[String] = {
table.StorageDescriptor.SerdeInfo.Parameters.get("parquet.column.index.access")
}
}
}
}
Is there a way to avoid creating a brand new object of TransformSettings in Transfomer class every time this is called?
Also is there a way to rewrite the code using Scala value class?
As #Dima proposed in the comments try to make TransformSettings a field / constructor parameter (a val) in the class Transformer and instantiate them outside
class TransformSettings{
def getOuputFormat: String = {
"orc"
}
def getOuputIndex(table: AWSGlueDDL.Table): Option[String] = {
table.StorageDescriptor.SerdeInfo.Parameters.get("orc.column.index.access")
}
}
class Transformer(val transformSettings: TransformSettings) {
def posttransform(table: AWSGlueDDL.Table): DataFrame ={
val indexAccess = transformSettings.getOuputIndex(table: AWSGlueDDL.Table)
???
}
}
val parquetTransformSettings = new TransformSettings {
override def getOuputFormat: String = {
"parquet"
}
override def getOuputIndex(table: AWSGlueDDL.Table): Option[String] = {
table.StorageDescriptor.SerdeInfo.Parameters.get("parquet.column.index.access")
}
}
class ParquetTransformer extends Transformer(parquetTransformSettings)
You don't seem to need value classes (... extends AnyVal) now. They are more about unboxing, not about life-cycle management. TransformSettings and Transformer can't be value classes because they are not final (you're extending them in class ParquetTransformer extends Transformer... and new TransformSettings { ... }). By the way, value classes have many limatations
https://failex.blogspot.com/2017/04/the-high-cost-of-anyval-subclasses.html
https://github.com/scala/bug/issues/12271
Besides value classes, there are scala-newtype library in Scala 2 and opaque types in Scala 3.
trait paymentTasks{
def calculatePayment()
def getData()
}
class paymentcalculator{
override def calculatePayment() ={
//Implementation }
}
override defGetData() ={
}
}
How do I call the method to keep the code less dependent and don't want to use new keyword
class MyApp{
val payment = new paymentcalculator
//I don't want to instantiate like this wants this to be decoupled
}
Please suggest how do I implement this ,also new Changes in future can be done without changing existing code.Also,suggest if doing this functionally would be more effective
If you want compile-time selection then just put the code into an object
trait PaymentTasks {
def calculatePayment()
def getData()
}
object PaymentCalculator extends PaymentTasks {
def calculatePayment() = ???
def getData() = ???
}
class MyApp {
val payment = PaymentCalculator.calculatePayment()
}
If you want run-time selection than the best way is to use dependency injection and create the payment object at the top level and pass it down to the code that requires it.
object SimplePaymentCalculator extends PaymentTasks
object ComplexPaymentCalculator extends PaymentTasks
class MyApp {
def myImplementation(tasks: PaymentTasks) = {
val payment = tasks.calculatePayment()
}
val payment =
if (???) {
SimplePaymentCalculator
} else {
ComplexPaymentCalculator
}
myImplementation(payment)
}
I'm trying to run a test with scaldi and specs2. In the test I need to override a StringManipulator function that uses an injected ProxyManipulator. The ProxyManipulator takes a string and returns its upper case in a Future. The replacement manipulator in the test returns a Future("Test Message").
Here is the StringManipulator class where the injection occurs:
class StringManipulator {
def manip (str : String) (implicit inj: Injector) : String = {
val prox = inject[ProxyManipulator]
Await.result(prox.manipulate(str), 1 second)
}
}
I'm using a package.object that contains the implicit injector:
import modules.MyModule
package object controllers {
implicit val appModule = new MyModule
}
And here is the specs2 test with the new binding:
#RunWith(classOf[JUnitRunner])
class StringManipScaldiSpec extends Specification {
class TestModule extends Module {
bind [ProxyManipulator] to new ProxyManipulator {
override def manipulate(name: String) = Future("Test Message")
}
}
"Application" should {
"do something" in {
val myTestModule = new TestModule
val str = "my string"
val stringMan = new StringManipulator() //(myTestModule)
stringMan.manip(str)(myTestModule) === "Test Message"
}
}
}
The problem is that when the test runs the class StringManipulator is still using the original Proxy Manipulator instead of the one passed in the TestModule. Any ideas?
I'm trying to add functionality to the scala.collection.Iterable trait, more specifically, a printerate function that iterates through the elements and prints them out (to the console if there are no parameters, otherwise to the outputstream param). I'm using a predefined extension method that I created for object, printSelf(). However, this is causing an compiler error, 'Value printSelf is not a member of type parameter Object.' I'd also like to have this an a separate file so that it's easy for me to use between several projects and applications.
Here's my current code for my conversion file:
import java.io.OutputStream
import scala.collection.Iterable
package conversion{
class Convert {
implicit def object2SuperObject(o:Object) = new ConvertObject(o)
implicit def iterable2SuperIterable[Object](i:Iterable[Object]) = new ConvertIterable[Object](i)
}
class ConvertObject(o:Object){
def printSelf(){
println(o.toString())
}
def printSelf(os:OutputStream){
os.write(o.toString().getBytes())
}
}
class ConvertIterable[Object](i:Iterable[Object]){
def printerate(){
i.foreach {x => x.printSelf() }
}
def printerate(os:OutputStream){
i.foreach { x => x.printSelf(os) }
}
}
}
I'm also getting a similar error in the code that's trying to test this out, 'value printerate is not a member of scala.collection.immutable.Range':
import conversion.Convert
package test {
object program extends App {
new testObj(10) test
}
class testObj(i: Integer) {
def test(){
val range = 0.until(i)
0.until(i).printerate()
}
}
}
What's wrong with the way that I'm approaching this type conversion?
Several things in fact:
Convert should be an object, not a class.
You use Object instead of Any
You use Object as a generic type identifier, instead of the much less confusing T.
You do not import the implicit definitions (it's not enough to import the object itself).
This should work:
package conversion {
object Convert {
implicit def object2SuperObject(o: Any) = new ConvertObject(o)
implicit def iterable2SuperIterable[T](i:Iterable[T]) = new ConvertIterable[T](i)
}
class ConvertObject(o: Any){
def printSelf(){
println(o.toString())
}
def printSelf(os:OutputStream){
os.write(o.toString().getBytes())
}
}
class ConvertIterable[T](i:Iterable[T]){
import Convert.object2SuperObject
def printerate(){
i.foreach {x => x.printSelf() }
}
def printerate(os:OutputStream){
i.foreach { x => x.printSelf(os) }
}
}
}
import conversion.Convert._
Second file:
package test {
object program extends App {
new testObj(10) test
}
class testObj(i: Integer) {
def test(){
val range = 0.until(i)
0.until(i).printerate()
}
}
}
Say, I'm using some json libraries which provides an object Json:
object Json {
def unapply[T](jsonStr:String)(implicit converter:Converter[T]) { ... }
}
I can use it in my scala code like this:
class MyLoginController {
def login(request:Request) = {
val loginInfo = Json.unapply[LoginInfo](request.body)
// check(loginInfo.email)
// check(loginInfo.password)
}
}
It's works perfectly, but soon I found my self can't test it easily with mock. I can't find a way to mock the Json.unapply.
So I have to change my code to provide a trait and use dependency injection:
trait JsonParsable[T] {
def parse(jsonStr:String)(implicit converter:Converter[T])
}
object JsonParser extends JsonParsable[T] {
def parse(jsonStr:String)(implicit converter:Converter[T]) = Json.unapply(jsonStr)
}
class MyLoginController(jsonParser:JsonParsable[T]) {
def login(request:Request) = {
val loginInfo = jsonParser.parse[LoginInfo](request.body)
// check(loginInfo.email)
// check(loginInfo.password)
}
}
And when I write unit test, I will mock a JsonParsable for the MyLoginController:
val fakeParser = mock[JsonParsable]
// ...
val controller = new MyLoginController(fakeParser)
// ...
My question is, do I have to do this to avoid using objects, just in order to make it testable?
I found previous code is simple and easy, but the later one is more complex :(