Does mockito check/match argument values in verify - scala

In this test case, I want to check that a function is called a specific no. of times with specific values
"add tag information in supported tags" in {
val servicesTestEnv = new ServicesTestEnv(components = components)
val questionTransactionDBService = new QuestionsTransactionDatabaseService(
servicesTestEnv.mockAnswersTransactionRepository,
servicesTestEnv.mockPartitionsOfATagTransactionRepository,
servicesTestEnv.mockPracticeQuestionsTagsTransactionRepository,
servicesTestEnv.mockPracticeQuestionsTransactionRepository,
servicesTestEnv.mockSupportedTagsTransactionRepository,
servicesTestEnv.mockUserProfileAndPortfolioTransactionRepository,
servicesTestEnv.mockQuestionsCreatedByUserRepo,
servicesTestEnv.mockTransactionService,
servicesTestEnv.mockPartitionsOfATagRepository,
servicesTestEnv.mockHelperMethods
)
when(servicesTestEnv.mockTransactionService.start)
.thenReturn(servicesTestEnv.mockDistributedTransaction)
doNothing().when(servicesTestEnv.mockPracticeQuestionsTransactionRepository).add(ArgumentMatchers.any[DistributedTransaction],ArgumentMatchers.any[PracticeQuestion],ArgumentMatchers.any[MutationCondition])
doNothing().when(servicesTestEnv.mockPracticeQuestionsTagsTransactionRepository).add(ArgumentMatchers.any[DistributedTransaction],ArgumentMatchers.any[PracticeQuestionTag],ArgumentMatchers.any[MutationCondition])
when(servicesTestEnv.mockPartitionsOfATagTransactionRepository.get(ArgumentMatchers.any[DistributedTransaction],ArgumentMatchers.any[TagPartitionKeys]))
.thenReturn(Right(servicesTestEnv.questionTestEnv.tagPartitions))
doNothing().when(servicesTestEnv.mockPartitionsOfATagTransactionRepository).add(ArgumentMatchers.any[DistributedTransaction],ArgumentMatchers.any[TagPartitions],ArgumentMatchers.any[MutationCondition])
doNothing().when(servicesTestEnv.mockQuestionsCreatedByUserRepo).add(ArgumentMatchers.any[DistributedTransaction],ArgumentMatchers.any[QuestionsCreatedByAUserForATag],ArgumentMatchers.any[MutationCondition])
when(servicesTestEnv.mockUserProfileAndPortfolioTransactionRepository.get(ArgumentMatchers.any[DistributedTransaction],ArgumentMatchers.any[ExternalUserProfileKeys]))
.thenReturn(Right(servicesTestEnv.externalUserProfileWithTags))
doNothing().when(servicesTestEnv.mockUserProfileAndPortfolioTransactionRepository).update(ArgumentMatchers.any[DistributedTransaction],ArgumentMatchers.any[ExternalUserProfile])
doNothing().when(servicesTestEnv.mockDistributedTransaction).commit()
when(servicesTestEnv.mockSupportedTagsTransactionRepository.get(ArgumentMatchers.any[DistributedTransaction],ArgumentMatchers.any[SupportedTagsKeys]))
.thenReturn(Left(SupportedTagNotFoundException()))
val supportedTagInfo = SupportedTag("coding","javascript1","empty")
logger.trace(s"will compare with ${supportedTagInfo}")
val result = questionTransactionDBService.newQuestion(servicesTestEnv.questionTestEnv.practiceQuestion,servicesTestEnv.questionTestEnv.practiceQuestionTag,servicesTestEnv.user)
verify(servicesTestEnv.mockSupportedTagsTransactionRepository,times(0))
.add(servicesTestEnv.mockDistributedTransaction,supportedTagInfo)
}
If I change the value of supportedTagInfo to val supportedTagInfo = SupportedTag("coding","javascript-something else","empty"), the test case still passes.
In the traces,I can see that in both times the tag coding-javascript-empty was used. This value comes from servicesTestEnv.questionTestEnv.practiceQuestionTag which is common for both test cases and is supplied at
val result = questionTransactionDBService.newQuestion(servicesTestEnv.questionTestEnv.practiceQuestion,servicesTestEnv.questionTestEnv.practiceQuestionTag,servicesTestEnv.user)
TRACE - saving coding-javascript-empty in supported tag information
Am I doing something wrong or does Mockito not check the argument values?
UPDATE
I tried using ArgumentCaptor in Scala but am struggling.
I have created mock of the class as
val mockSupportedTagsTransactionRepository = mock(classOf[SupportedTagsTransactionRepository])
I am calling add method of the mock. Its signature is
def add(transaction:DistributedTransaction,supportedTag:SupportedTag,mutationCondition:MutationCondition = new PutIfNotExists()) = {...}
I call get and add methods of the above mock. I have defined their behaviour as
when(servicesTestEnv.mockSupportedTagsTransactionRepository.get(ArgumentMatchers.any[DistributedTransaction],ArgumentMatchers.any[SupportedTagsKeys]))
.thenReturn(Left(SupportedTagNotFoundException()))
Then I create the required ArgumentCaptor
val argumentCaptor2 = ArgumentCaptor.forClass(classOf[SupportedTag])
val argumentCaptor3 = ArgumentCaptor.forClass(classOf[MutationCondition])
and then invoke the function under test
verify(servicesTestEnv.mockSupportedTagsTransactionRepository ,times(1))
.add(argumentCaptor1.capture(),argumentCaptor2.capture(),argumentCaptor3.capture())
logger.trace(s"capture 1 ${argumentCaptor1.getAllValues}")
logger.trace(s"capture 2 ${argumentCaptor2.getAllValues}")
logger.trace(s"capture 3 ${argumentCaptor3.getAllValues}")
Then I check the result
val argumentsInvoked = argumentCaptor2.getAllValues
argumentsInvoked.contains(supportedTagInfo)
mustBe true
But argumentsInvoked type is List[Nothing] instead of List[SupportedTag]

The right way is to also specify the type of argument
val argumentCaptor1 = ArgumentCaptor.forClass(classOf[DistributedTransaction])
val argumentCaptor2 = ArgumentCaptor.forClass[SupportedTag,SupportedTag](classOf[SupportedTag]) //Note two types. First is type of argument, second is type of class. They are the same in my case.
val argumentCaptor3 = ArgumentCaptor.forClass(classOf[MutationCondition])
verify(servicesTestEnv.mockSupportedTagsTransactionRepository ,times(1))
.add(argumentCaptor1.capture(),argumentCaptor2.capture(),argumentCaptor3.capture())
val argumentsInvoked = argumentCaptor2.getAllValues //this now returns List[SupportedTag]
argumentsInvoked.size mustBe 1
val argument = argumentsInvoked.get(0)
argument.course mustBe supportedTagInfo.course
argument.subject mustBe supportedTagInfo.subject
argument.topic mustBe supportedTagInfo.topic

Related

while comparing the two objects in scala not getting correct result

Pojo classes
case class RulePojoMigration(val tolerance_id:Int,val asset_id:Int, measure: String, cond: String, function: String, threshold_value: String, rule_dts: String,
filter: util.List[Filter], is_enabled: String, mode: String, tolerance_status: String, action_key: String,
email_ids: util.List[String], rule_name:String, rule_owner:String, group_by: util.List[String], relax_variation_band:String, reconciliation_id:Int)
case class Filter(value: String, operator: String, dimension: String)
Below is the code used to fetch the object from DB
if (checkRuleIsEditOrNot) {
ps = con.prepareStatement(WSQueryConstant.RULE_MIGRATION_GET_COMPLETE_RULE_INFO_FROM_STAGE)
ps.setInt(1, rulePojo.tolerance_id)
rs = ps.executeQuery
var dbRulePojo: RulePojoMigration = null
val jsonRulePojo: RulePojoMigration = rulePojo
if (rs.next()) {
val rule_tolerance_asset_id = rs.getInt(2)
val measure = rs.getString(3)
val cond = rs.getString(4)
val function = rs.getString(5)
val threshold_value = rs.getString(6)
val rule_dts = rs.getString(7)
val filter = gson.fromJson(rs.getString(8), classOf[util.List[Filter]])
val is_enabled = rs.getString(9)
val mode = rs.getString(10)
val tolerance_status = rs.getString(11)
val email_ids = gson.fromJson(rs.getString(12), classOf[java.util.List[String]])
val rule_name = rs.getString(13)
val rule_owner = rs.getString(14)
val group_by = gson.fromJson(rs.getString(15), classOf[java.util.List[String]])
val relax_variation_band = rs.getString(16)
var reconciliation_id = rs.getString(17)
if(reconciliation_id== null)
reconciliation_id= "-1"
dbRulePojo = RulePojoMigration(jsonRulePojo.tolerance_id, rule_tolerance_asset_id, measure, cond, function, threshold_value, rule_dts, filter, is_enabled, mode, tolerance_status, jsonRulePojo.action_key, email_ids, rule_name, rule_owner, group_by, relax_variation_band, reconciliation_id.toInt)
user request with the below values.
RulePojoMigration(1274,1234,*,less than,count,100,2020-07-04 05:31:29,[Filter(Hello,equal,funnel_state)],1,static,null,EMAIL,[x],StaticRuleMigrationTesting,email,[ALL],0,-1)
Object available in MySQL database
RulePojoMigration(1274,1234,*,less than,count,100,2020-07-04 05:31:29,[{value=Hello, operator=equal, dimension=funnel_state}],1,static,null,EMAIL,[x],StaticRuleMigrationTesting,email,[ALL],0,-1)
So I want to check the equality of two objects so that the user can modify the the existing object if there is any changes in the payload or simply he/she should get a message saying duplicate entry exist. jsonRulePojo.equal(dbRulePojo) should be true, anyhow values are same of both object.
The default equals operation does not check the contents of the two classes, it just checks whether both values refer to the same object. So two instances of RulePojoMigration will always be different even if every field is the same. By default two classes are only equal if they are the same instance.
When a case class is defined, Scala will generate an equals that does check each of the fields passed to the constructor in turn. So two case classes will be equal if they have the same contents.
So you can fix this code in one of two ways:
Make RulePojoMigration a case class
Add an equals method to RulePojoMigration
I am able to resolve the bug by making the below changes.
From
val filter = gson.fromJson(rs.getString(8), classOf[util.List[Filter]])
To
val filter = gson.fromJson(rs.getString(8), classOf[Array[Filter]])
and
dbRulePojo = RulePojoMigration(jsonRulePojo.tolerance_id, rule_tolerance_asset_id, measure, cond, function, threshold_value, rule_dts, java.util.Arrays.asList(filter:_*)
, is_enabled, mode, tolerance_status, jsonRulePojo.action_key, email_ids, rule_name, rule_owner, group_by, relax_variation_band, reconciliation_id.toInt)
And instead of passing the filter directly into dbRulePojo i passed the java.util.Arrays.asList(filter:_*)

unable to convert a java.util.List into Scala list

I want that the if block returns Right(List[PracticeQuestionTags]) but I am not able to do so. The if/else returns Either
//I get java.util.List[Result]
val resultList:java.util.List[Result] = transaction.scan(scan);
if(resultList.isEmpty == false){
val listIterator = resultList.listIterator()
val finalList:List[PracticeQuestionTag] = List()
//this returns Unit. How do I make it return List[PracticeQuestionTags]
val answer = while(listIterator.hasNext){
val result = listIterator.next()
val convertedResult:PracticeQuestionTag = rowToModel(result) //rowToModel takes Result and converts it into PracticeQuestionTag
finalList ++ List(convertedResult) //Add to List. I assumed that the while will return List[PracticeQuestionTag] because it is the last statement of the block but the while returns Unit
}
Right(answer) //answer is Unit, The block is returning Right[Nothing,Unit] :(
} else {Left(Error)}
Change the java.util.List list to a Scala List as soon as possible. Then you can handle it in Scala fashion.
import scala.jdk.CollectionConverters._
val resultList = transaction.scan(scan).asScala.toList
Either.cond( resultList.nonEmpty
, resultList.map(rowToModel(_))
, new Error)
Your finalList: List[PracticeQuestionTag] = List() is immutable scala list. So you can not change it, meaning there is no way to add, remove or do change to this list.
One way to achieve this is by using scala functional approach. Another is using a mutable list, then adding to that and that list can be final value of if expression.
Also, a while expression always evaluates to Unit, it will never have any value. You can use while to create your answer and then return it seperately.
val resultList: java.util.List[Result] = transaction.scan(scan)
if (resultList.isEmpty) {
Left(Error)
}
else {
val listIterator = resultList.listIterator()
val listBuffer: scala.collection.mutable.ListBuffer[PracticeQuestionTag] =
scala.collection.mutable.ListBuffer()
while (listIterator.hasNext) {
val result = listIterator.next()
val convertedResult: PracticeQuestionTag = rowToModel(result)
listBuffer.append(convertedResult)
}
Right(listBuffer.toList)
}

Scio Apache Beam - How to properly separate a pipeline code?

I have a pipeline with a set of PTransforms and my method is getting very long.
I'd like to write my DoFns and my composite transforms in a separate package and use them back in my main method. With python it's pretty straightforward, how can I achieve that with Scio? I don't see any example of doing that. :(
withFixedWindows(
FIXED_WINDOW_DURATION,
options = WindowOptions(
trigger = groupedWithinTrigger,
timestampCombiner = TimestampCombiner.END_OF_WINDOW,
accumulationMode = AccumulationMode.ACCUMULATING_FIRED_PANES,
allowedLateness = Duration.ZERO
)
)
.sumByKey
// How to write this in an another file and use it here?
.transform("Format Output") {
_
.withWindow[IntervalWindow]
.withTimestamp
}
If I understand your question correctly, you want to bundle your map, groupBy, ... transformations in a separate package, and use them in your main pipeline.
One way would be to use applyTransform, but then you would end up using PTransforms, which are not scala-friendly.
You can simply write a function that receives an SCollection and returns the transformed one, like:
def myTransform(input: SCollection[InputType]): Scollection[OutputType] = ???
But if you intend to write your own Source/Sink, take a look at the ScioIO class
You can use map function to map your elements example.
Instead of passing a lambda, you can pass a method reference from another class
Example .map(MyClass.MyFunction)
I think one way to solve this could be to define an object in another package and then create a method in that object that would have the logic required for your transformation. For example:
def main(cmdlineArgs: Array[String]): Unit = {
val (sc, args) = ContextAndArgs(cmdlineArgs)
val defaulTopic = "tweets"
val input = args.getOrElse("inputTopic", defaulTopic)
val output = args("outputTopic")
val inputStream: SCollection[Tweet] = sc.withName("read from pub sub").pubsubTopic(input)
.withName("map to tweet class").map(x => {parse(x).extract[Tweet]})
inputStream
.flatMap(sentiment.predict) // object sentiment with method predict
}
object sentiment {
def predict(tweet: Tweet): Option[List[TweetSentiment]] = {
val data = tweet.text
val emptyCase = Some("")
Some(data) match {
case `emptyCase` => None
case Some(v) => Some(entitySentimentFile(data)) // I used another method, //not defined
}
}
Please also this link for an example given in the Scio examples

passing a code block to method without execution

I have following code:
import java.io._
import com.twitter.chill.{Input, Output, ScalaKryoInstantiator}
import scala.reflect.ClassTag
object serializer {
val instantiator = new ScalaKryoInstantiator
instantiator.setRegistrationRequired(false)
val kryo = instantiator.newKryo()
def load[T](file:_=>_,name:String,cls:Class[T]):T = {
if (java.nio.file.Files.notExists(new File(name).toPath())) {
val temp = file
val baos = new FileOutputStream(name)
val output = new Output(baos, 4096)
kryo.writeObject(output, temp)
temp.asInstanceOf[T]
}
else {
println("loading from " + name)
val baos = new FileInputStream(name)
val input = new Input(baos)
kryo.readObject(input,cls)
}
}
}
I want to use it in this way:
val mylist = serializer.load((1 to 100000).toList,"allAdj.bin",classOf[List[Int]])
I don't want to run (1 to 100000).toList every time so I want to pass it to the serializer and then decide to compute it for the first time and serialize it for future or load it from file.
The problem is that the code block is running first in my code, how can I pass the code block without executing it?
P.S. Is there any scala tool that do the exact thing for me?
To have parameters not be evaluated before being passed, use pass-by-name, like this:
def method(param: =>ParamType)
Whatever you pass won't be evaluated at the time you pass, but will be evaluated each time you use param, which might not be what you want either. To have it be evaluated only the first time you use, do this:
def method(param: =>ParamType) = {
lazy val p: ParamType = param
Then use only p on the body. The first time p is used, param will be evaluated and the value will be stored. All other uses of p will use the stored value.
Note that this happens every time you invoke method. That is, if you call method twice, it won't use the "stored" value of p -- it will evaluate it again on first use. If you want to "pre-compute" something, then perhaps you'd be better off with a class instead?

How do I use Scala Hashmaps and Tuples together correctly?

My code is as follows
import scala.collection.mutable.HashMap
type CrossingInterval = (Date, Date)
val crossingMap = new HashMap[String, CrossingInterval]
val crossingData: String = ...
Firstly why does the following line compile?
val time = crossingMap.getOrElse(crossingData, -1)
I would have thought -1 would have been an invalid value
Secondly how do I do a basic check such as the following
if (value exists in map) {
}
else {
}
In Java I would just check for null values. I'm not sure about the proper way to do it in Scala
Typing your code in the interpreter shows why the first statement compiles:
type Date = String
scala> val time = crossingMap.getOrElse(crossingData, -1)
time: Any = -1
Basically, getOrElse on a Map[A, B] (here B = CrossingDate) accepts a parameter of any type B1 >: B: that means that B1 must be a supertype of B. Here B1 = Any, and -1 is of course a valid value of type Any. In this case you actually want to have a type declaration for time.
For testing whether a key belongs to the map, just call the contains method. An example is below - since Date was not available, I simply defined it as an alias to String.
scala> crossingMap.contains(crossingData)
res13: Boolean = false
scala> crossingMap += "" -> ("", "")
res14: crossingMap.type = Map("" -> ("",""))
//Now "" is a map of the key
scala> crossingMap.contains("")
res15: Boolean = true
If you want to check whether a value is part of the map, the simplest way is to write this code:
crossingMap.values.toSet.contains("")
However, this builds a Set containing all values. EDIT: You can find a better solution for this subproblem in Kipton Barros comment.