mockK cannot differentiate types in every statements - kotlin

I am writing a method in Kotlin which returns elasticsearch indices that have an alias assigned to them:
fun getActiveIndices(cluster: ElasticsearchCluster): List<IndexModel> {
val aliases = elasticsearchCommandExecutor.execute(GetAllAliasesCommand(cluster))
val indices = elasticsearchCommandExecutor.execute(GetAllIndicesCommand(cluster))
indices.forEach{ it.active = aliases.any { alias -> it.name == alias.index } }
return indices.filter { !it.irregular && it.active }
}
Where GetAllAliasesCommand and GetAllIndicesCommand are subclasses of ElasticsearchCommand<T>.
I am trying to test this method's behavior using mockK:
#Test
fun `getActiveIndices should make correct calls`() {
val aliases = listOf(.. A list of AliasModel)
val indices = listOf(.. A list of IndexModel)
every { elasticsearchCommandExecutor.execute(any<GetAllAliasesCommand>()) } returns aliases
every { elasticsearchCommandExecutor.execute(any<GetAllIndicesCommand>()) } returns indices
val result = indexService.getActiveIndices(ElasticsearchCluster.SOME_CLUSTER)
verify { elasticsearchCommandExecutor.execute(any<GetAllAliasesCommand>()) }
verify { elasticsearchCommandExecutor.execute(any<GetAllIndicesCommand>()) }
assert(result == listOf(.. A list of IndexModel))
}
The problem is that mockK cannot differentiate between any<GetAllIndicesCommand>() and any<GetAllAliasesCommand>() in every statement so both elasticsearchCommandExecutor.execute(any<GetAllIndicesCommand>()) and
elasticsearchCommandExecutor.execute(any<GetAllAliasesCommand>()) returns indices. Which means it applies the last every statement. Is there a way to make it return based on command type?

Turns out I had to use ofType matcher for this purpose. So the final code is:
#Test
fun `getActiveIndices should make correct calls`() {
val aliases = listOf(.. A list of AliasModel)
val indices = listOf(.. A list of IndexModel)
every { elasticsearchCommandExecutor.execute(ofType(GetAllAliasesCommand::class)) } returns aliases
every { elasticsearchCommandExecutor.execute(ofType(GetAllIndicesCommand::class)) } returns indices
val result = indexService.getActiveIndices(ElasticsearchCluster.SOME_CLUSTER)
verify { elasticsearchCommandExecutor.execute(ofType(GetAllIndicesCommand::class)) }
verify { elasticsearchCommandExecutor.execute(ofType(GetAllAliasesCommand::class)) }
assert(result == listOf(.. A list of IndexModel))
}

Related

In Kotlin, how can I test and use a value without computing it twice?

Every so often, I find myself wanting to compute a value for some sort of filter operation, but then wanting to use that value when it's already disappeared into the condition-checking thing.
For instance:
val found = list.firstOrNull { slowConversion(it).isWanted() }
if (found != null) {
something(found, slowConversion(found))
}
or
when {
other_conditions -> other_actions
list.any { it.contains(regex1) } -> something(list.firstOrNull { it.contains(regex1) } ?: "!!??")
}
For the slowConversion() I can work with a sequence mapped to pairs, although the terms first and second kinda confuse things a bit...
val pair = list.asSequence().map { it to slowConversion(it) }.firstOrNull { it.second.isWanted() }
if ( pair != null ) {
something(pair.first, pair.second)
}
or if I only want the conversion,
val converted = list.firstNotNullOfOrNull { slowConversion(it).takeIf { it.isWanted() } }
but the best I can come up with to avoid the when duplication involves moving the action part into the condition part!
fun case(s: List<String>, r: Regex) {
val match = s.firstOrNull { it.contains(r) }?.also { something(it) }
return match != null
}
when {
other_conditions -> other_actions
case(list, regex1) -> true
}
At this point, it seems I should just have a stack of function calls linked together with ||
other_things || case(list, regex1) || case(list, regex2) || catchAll(list)
Is there something better or more concise for either of these?
You can write your first example like this:
for(element in list) {
val result = slowConversion(element)
if(result.isWanted()) {
something(element, result)
break
}
}
This might not look very Kotlin-ish, but I think it's pretty straightforward & easy to understand.
For your second example, you can use the find function:
when {
other_conditions -> other_actions
else -> list.find { it.contains(regex1) }?.let(::something)
}
If you have multiple regexes, just iterate over them,
val regexes = listOf(regex1, regex2, ...)
for(regex in regexes) {
val element = list.find { it.contains(regex1) } ?: continue
something(element)
break
}

Kotlin functional find single element

I am relatively new to Kotlin and I try to overcome a special case.
I am filtering a books store and want to verify that the length of the obtained list is exactly one unit shorter than the original one. Further I need to verify that the discarded element is under a specific state. Here is my example:
fun BookStoreVerified(bookStore: BookStore): Boolean {
val specialChapter = bookStore.stores
.flatMap { it.books }
.flatMap { it.chapters }.filter { it != null && it.state == Chapter.SPECIAL }
val total = bookStore.stores
.flatMap { it.books }
.flatMap { it.chapters }
.filterNotNull()
val finalChapters = book.stores
.flatMap { it.books }
.flatMap { it.chapters }
.filter { it != null && it.state.isCorrect }
return (finalChapters.size + specialChapterFigure.size == total.size) && (specialChapter.size == 1)
}
My question is if there is a smarter way to compute the above operation. I would like to know if ander a scope like filter, map can we make reference to the previous object? ( get the length of the original list for instance ?)
You have Books where each Book contains a list of Chapters. You want to partition chapters from all the books according to some criteria.
With this in mind the partition function can be useful:
data class Chapter(val state: String)
data class Book(val chapters: List<Chapter>? = null)
fun main() {
val books = listOf(
Book(),
Book(chapters = listOf(Chapter("a"), Chapter("SPECIAL"))),
Book(chapters = listOf(Chapter("c"), Chapter("d")))
)
val (specialChs, regularChs) = books
.flatMap { it.chapters ?: emptyList() }
.partition { it.state == "SPECIAL" }
println(specialChs) // [Chapter(state=SPECIAL)]
println(regularChs) // [Chapter(state=a), Chapter(state=c), Chapter(state=d)]
}
Now that you have specialChs and regularChs, you can check whatever invariants you want.
For example:
check(specialChs.size == 1 && specialChs.first().state ==
"SPECIAL")
Edit: It is possible to abstract away the existence of null chapters inside a Book:
data class Book(val chapters: List<Chapter>? = null) {
val safeChapters: List<Chapter>
get() = chapters ?: emptyList()
}
then in your code you can flatMap { it.safeChapters } instead of .flatMap { it.chapters ?: emptyList() }

Kotlin - adding map function conditionally

I would like to feature toggle a map function on a list. I have a map that I would like to run only if the feature is on:
So for something like this:
items
.map { doTransformation(it) }
.map { runOnlyIfFeatureIsOn(it) }
Is there a way of adding the whole .map function conditionally in kotlin, so that it is only there if it is feature toggled?
let() is handy for doing arbitrary processing in a pipeline, e.g.:
items
.map{ doTransformation(it) }
.let{ if (someCondition) it.map{ runOnlyIfFeatureIsOn(it) } else it }
(For complex/costly conditions, this will be more efficient than putting the if inside the map call, as this'll only evaluate the condition once.)
Maybe just do if in map? There is no problem with that:
val list = listOf(1, 2, 3)
list
.map { it * 2 }
.map {
if (featureIsOn) {
runFeatureMapping(it)
} else {
it
}
}
Using sequences:
var sequence = items.asSequence()
.map { doTransformation(it) }
if (<feature_1_enabled>) {
sequence = sequence.map { runOnlyIfFeature1IsOn(it) }
}
if (<feature_2_enabled>) {
sequence = sequence.map { runOnlyIfFeature2IsOn(it) }
}
val result = sequence.toList()
Sequences are lazy-evaluated and should be used when mutliple operations (filter/map/etc) are applied

How to hand over Boolean operator as parameter in Kotlin?

I have a function which has quite a lot lines. In that function I have a .filter{} like:
fun getMyListForFoo(): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter{ it.flag == Query.IS_FOO }
.map{
//..mappings
}
}
and then I have a second function just to retrieve queries which are NOT Foo:
fun getMyListForNotFoo(): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter{ it.flag != Query.IS_FOO }
.map{
//..mappings
}
}
As you can the only difference is the == or != operator in the .filter function. Although I have all the previous lines duplicated..
I bet there is a nice Kotlin way to enhance this code?
Pass a predicate as a parameter to your function for filtering the list.
fun getMyList(predicate: (YourType) -> Boolean): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter(predicate)
.map{
//..mappings
}
}
Usage:
val listForFoo = getMyList { it.flag == Query.IS_FOO }
val listForNotFoo = getMyList { it.flag != Query.IS_FOO }
OR, if you just want to pass a Boolean, you can also do that:
fun getMyList(filterFoo: Boolean): List<Blub> {
//.. lot of lines
return myRepo.queryList()
.filter {
val isFoo = it.flag == Query.IS_FOO
if(filterFoo) isFoo else !isFoo
}
.map{
//..mappings
}
}
I would use partition directly.
I created a sample in kotlinlang.org's playground and it looks like this:
// Given a "thing"
data class Thing(val id: Int, val isFoo: Boolean)
// Have a function that simplifies this:
fun filterThings(source: List<Thing>) = source.partition { it.isFoo }
// Alternatively, you could have a more generic one:
fun filterThings(source: List<Thing>,
predicate: ((Thing) -> Boolean)) = source.partition(predicate)
// And you can use either like so:
// Given the source
val source = listOf(Thing(1, true),
Thing(2, true),
Thing(3, false),
Thing(4, true),
Thing(5, false),
Thing(6, false))
// Filter them with the non-configurable version:
val results = filterThings(source)
// or the more configurable one where *you* supply the predicate:
val results = filterThings(source) { it.isFoo }
The results are going to be:
results.first is going to be the one that pass the predicate, and the rest will be in results.second:
results.first = [Thing(id=1, isFoo=true), Thing(id=2, isFoo=true), Thing(id=4, isFoo=true)]
results.second = [Thing(id=3, isFoo=false), Thing(id=5, isFoo=false), Thing(id=6, isFoo=false)]

incrementing hash map count in Kotlin

I have the function below. However, when I pass a string to it, I get the following error:
error: operator call corresponds to a dot-qualified call 'charCountMap.get(c).plus(1)' which is not allowed on a nullable receiver 'charCountMap.get(c)'. charCountMap.put(c, charCountMap.get(c) + 1)
private fun characterCount(inputString:String) {
val charCountMap = HashMap<Char, Int>()
val strArray = inputString.toCharArray()
for (c in strArray)
{
if (charCountMap.containsKey(c))
{
charCountMap.put(c, charCountMap.get(c) + 1)
}
else
{
charCountMap.put(c, 1)
}
}
}
The Kotlin Standard Library has groupingBy and eachCount for this purpose, you don't need to do any of this manually:
private fun characterCount(inputString:String) {
val charCountMap : Map<Char, Int> = inputString.groupingBy { it }.eachCount()
}
Note that I put the type on charCountMap for clarity, but it can be left off and inferred.
There is nice compute method in HashMap for this:
private fun characterCount(inputString:String) = hashMapOf<Char, Int>().also { charCountMap ->
inputString.forEach { charCountMap.compute(it) { _, v -> if (v == null) 1 else v + 1 } }
}
Both the other answers are correct. Todd's answer is right, you don't need to write a function for this. Just use the standard library. And if you are going to write a function that updates maps, Михаил Нафталь's suggestion to use compute() to handle updating existing values is also good.
However, if you're just doing this an an exercise, here are three suggestions to fix/improve your algorithm:
Instead of get(), use getValue(), which does not return null. It will raise an exception if the element does not exist, but you already checked for that.
Use the [] operator instead of put() (no need to, it's just nicer syntax).
You don't need to call toCharArray() because Strings are already iterable.
if (charCountMap.containsKey(c))
{
charCountMap[c] = charCountMap.getValue(c) + 1
}
else
{
charCountMap[c] = 1
}
Rewriting the whole thing using standard formatting:
fun characterCount(inputString: String): Map<Char, Int> {
val charCountMap = mutableMapOf<Char, Int>()
for (c in inputString) {
if (charCountMap.containsKey(c)) {
charCountMap[c] = charCountMap.getValue(c) + 1
} else {
charCountMap[c] = 1
}
}
return charCountMap
}