I am trying to mock the awscala.dynamodbv2.DynamoDB.putConditionalMethod
How would one define an expects for a method which is curried and includes a repeated parameter:
putConditional(tableName: String, attributes: (String, Any)*)(cond: Seq[(String, aws.model.ExpectedAttributeValue)]): Unit
Here's what I've got working:
(mockClient.putConditional(_: String, _: (String, Any))(_: Seq[(String, ExpectedAttributeValue)]))
.expects("Data-Identity-Partitions",
*,
Seq(
"DatacenterId" -> exp.isNull,
"InstanceId" -> exp.isNull,
"TTL" -> exp.isNull
))
But this:
(mockClient.putConditional(_: String, _: (String, Any))(_: Seq[(String, ExpectedAttributeValue)]))
.expects("Data-Identity-Partitions",
Seq("DatacenterId" -> 1,
"InstanceId" -> 0,
"TTL" -> System.currentTimeMillis()),
Seq(
"DatacenterId" -> exp.isNull,
"InstanceId" -> exp.isNull,
"TTL" -> exp.isNull
))
results in the following compiler error:
[error] AwsPartitionActorSpec.scala:76: type mismatch;
[error] found : Seq[(String, Any)]
[error] required: org.scalamock.matchers.MockParameter[(String, Any)]
[error] Seq[(String, Any)]("DatacenterId" -> 1,
[error] ^
better late than never i suppose, here's my suggestion:
trait testtrait {
def foo(t: String, a: (String, Any) *): Int
}
"foo" should "work" in {
val m = mock[testtrait]
m.foo _ expects where {
case ("foo", Seq(("bar", 42L), ("baz", "mango"))) => true
case _ => false
} returns 5
m.foo("foo", ("bar", 42L), ("baz", "mango")) should be (5)
}
Related
I'm having some difficulties trying to understand what is going on the following code:
fun helperMethodNameA(someId: String, rules: RulesObject) {
val content = JsonNodeFactory.instance.arrayNode().apply { // A & B
add(JsonNodeFactory.instance.objectNode().apply {
set("body", JsonNodeFactory.instance.objectNode().apply { // C
set("text", JsonNodeFactory.instance.objectNode().apply { // D
set("value", JsonNodeFactory.instance.textNode(mapper.writeValueAsString(rules))) // E
})
})
})
}
return helperMethodNameB(someId, content.toString())
}
This project has a dependency on another which set Kotlin v1.3.20. The dependency project had the Kotlin version bumped up to v1.3.60. This bit broke with the update as per the following:
A - [ERROR] <pathToFile> [line, position] Type inference failed: inline fun <T> T.apply(block: T.() -> Unit): T
[ERROR] cannot be applied to
[ERROR] receiver: ArrayNode! arguments: (ArrayNode!.() -> ArrayNode!)
B - [ERROR] <pathToFile> [line, position] Type mismatch: inferred type is ArrayNode!.() -> ArrayNode! but ArrayNode!.() -> Unit was expected
C - [ERROR] <pathToFile> [line, position] Type inference failed: Not enough information to infer parameter T in operator fun <T : JsonNode!> set(p0: String!, p1: JsonNode!): T!
[ERROR] Please specify it explicitly.
D - [ERROR] <pathToFile> [line, position] Type inference failed: Not enough information to infer parameter T in operator fun <T : JsonNode!> set(p0: String!, p1: JsonNode!): T!
[ERROR] Please specify it explicitly.
E - [ERROR] <pathToFile> [line, position] Type inference failed: Not enough information to infer parameter T in operator fun <T : JsonNode!> set(p0: String!, p1: JsonNode!): T!
[ERROR] Please specify it explicitly.
What am I missing here?
The solution was to specify the type as bellow:
fun helperMethodNameA(someId: String, rules: RulesObject) {
val content = JsonNodeFactory.instance.arrayNode().apply {
add(JsonNodeFactory.instance.objectNode().apply {
set<ObjectNode>("body", JsonNodeFactory.instance.objectNode().apply {
set<ObjectNode>("text", JsonNodeFactory.instance.objectNode().apply {
set<TextNode>("value", JsonNodeFactory.instance.textNode(mapper.writeValueAsString(rules)))
})
})
})
}
return helperMethodNameB(someId, content.toString())
}
I have a function
fun <T> get(path: String, params: MutableMap<String, Any>? = null, headers: MutableMap<String, String>? = null, resolver: ResponseResolver<T>): HttpRequest<T>
which ResponseResolver is a type alias
typealias ResponseResolver<T> = (HttpResponse) -> T
When i invoke the get method like below:
get("/somePath", mutableMapOf("key" to "value")){ httpResponse -> ......some code(Last line is a List<SomeClass>)
Then the Intellij tells me that
Type inference failed:
fun <T> get
(
path: String,
params: MutableMap<String, Any>? = ...,
headers: MutableMap<String, String>? = ...,
resolver: ResponseResolver<T> /* = (HttpResponse) → T */
)
: HttpRequest<T>
cannot be applied to
(
String,
MutableMap<String, Any>,
(HttpResponse) → List<SomeClass>
)
I'm not sure if there is any strictions in applying the closure as the argument of some functions with default parameters.
Kotlin don't know exactly what mutableMapOf("key" to "value") is.
Clarify whether it's params or headers
get("/somePath", headers = mutableMapOf("key" to "value")){ httpResponse -> ......some code(Last line is a List<SomeClass>)
or
get("/somePath", mutableMapOf<String, Any>("key" to "value")){ httpResponse -> ......some code(Last line is a List<SomeClass>)
I am trying to use the Option.getOrElse() method.
According to the source:
inline fun <R> fold(ifEmpty: () -> R, ifSome: (A) -> R): R = when (this) {
is None -> ifEmpty()
is Some<A> -> ifSome(t)
}
fun <T> Option<T>.getOrElse(default: () -> T): T = fold({ default() }, ::identity)
But when I call getOrElse with a lambda that returns a value of type other than type T, it does not show any error compile time or runtime.
val value1 = Some("val")
// No error
value1.getOrElse { true }
It does not seem right. What am I doing wrong?
This is because Option is covariant (you can see it's declared as Option<out A>), so value1 is also an Option<Any> and { true } is inferred to be () -> Any.
Given the following Kotlin code:
class Foo<T>(val t : T?)
fun <T : Any, R : Any> Foo<T?>.transform(transformer : (T) -> R) : Foo<R?> {
return when (t) {
null -> Foo(null)
else -> Foo(transformer(t))
}
}
fun main(args : Array<String>) {
val foo = Foo(args.firstOrNull())
val bar = foo.transform<String, Int> { t -> t.length }
val baz = bar.transform<Int, IntRange> { t -> t..(t + 1) }
}
Why do I get the following error:
Type mismatch. Required: Foo<String?> Found: Foo<String>
If I remove the ? from the extension function to be Foo<T>.transform I instead get the following error:
Type mismatch. Required: Foo<Int> Found: Foo<Int?>
I can understand the second error, because you cannot assign Int? to Int, but the first doesn't make any sense, as you can assign String to String?
EDIT:
I have modified the class Foo<T> to be class Foo<out T> and this works for me as the value t will only ever be read after the initial assignment. With this option I do not need to define the type parameters at the call site of transform.
Another option I have found that I think is a bit messy (and not sure why it makes a difference) is adding a third type parameter to the extension function as follows:
fun <T : Any, U : T?, R : Any> Foo<U>.transform(transformer : (T) -> R) : Foo<R?>
The call site of this on the other hand I find a bit odd. Looking at the above code the call of foo.transform MUST NOT include the type parameters, but the call of bar.transform<Int, Int?, IntRange> MUST include the type parameters in order to work.
This option allows setting the value t at some later point if it were a var instead of val. But it also removes the smart casting on t in the transform function. Although that can be gotten around with a !! if you are not worried about race conditions or (with some additional effort) ?: or ?. if you are worried about race conditions.
You can change your Foo<T> class to be not invariant (see https://kotlinlang.org/docs/reference/generics.html):
class Foo<out T>(val t : T?)
fun <T : Any, R : Any> Foo<T?>.transform(transformer : (T) -> R) : Foo<R?> {
return when (t) {
null -> Foo(null)
else -> Foo(transformer(t))
}
}
fun main(args : Array<String>) {
val foo = Foo(args.firstOrNull())
val bar = foo.transform<String, Int> { t -> t.length }
val baz = bar.transform<Int, IntRange> { t -> t..(t + 1) }
}
The out T specifies precisely the behavior you want.
Since you specify the property t in the constructor as T? you don't need to specify Foo<T?> as receiver and Foo<R?> as return type. Instead use Foo<T> and Foo<R> and it will work.
class Foo<T>(val t : T?)
fun <T: Any, R: Any> Foo<T>.transform(transformer : (T) -> R) : Foo<R> {
return when (t) {
null -> Foo(null)
else -> Foo(transformer(t))
}
}
fun main(args : Array<String>) {
val foo = Foo(args.firstOrNull())
val bar = foo.transform { t -> t.length }
val baz = bar.transform { t -> t..(t + 1) }
}
Note: You don't need to specify the generic types for transform because they can be inferred (at least in this example).
Currently I am using compose from a library called arrow which has it defined this way.
inline infix fun <IP, R, P1> ((IP) -> R).compose(crossinline f: (P1) -> IP): (P1) -> R = { p1: P1 -> this(f(p1)) }
What I am trying to do is compose functions from a list so I assumed something as simple as this would work.
val add5 = { i: Int -> Option(i + 5) }
val multiplyBy2 = { i: Int -> i * 2 }
fun isOdd(x: Option<Int>) = x.map { y -> y % 2 != 0 }
val composed = listOf(::isOdd, add5, multiplyBy2).reduce { a, b -> a compose b }
but I get type error:
Type inference failed: Cannot infer type parameter IP in inline infix
fun ((IP) -> R).compose(crossinline f: (P1) -> IP): (P1)
-> R None of the following substitutions receiver: (Any) -> Any arguments: ((Nothing) -> Any) receiver: (Nothing) -> Any arguments:
((Nothing) -> Nothing) can be applied to receiver: Function1<, Any>
arguments: (Function1<, Any>)
so I try:
val composed = listOf<(Any) -> Any>(::isOdd, add5, multiplyBy2).reduce { x, y -> x compose y }
and I get this:
Type mismatch: inferred type is KFunction1<#ParameterName Option, Option> but (Any) -> Any was expected
Type mismatch: inferred type is (Int) -> Option but (Any) -> Any was expected
Type mismatch: inferred type is (Int) -> Int but (Any) -> Any was expected
Any help appreciated. I don't mind if I end up having to write my own version of compose. I just need to be able to compose a list of functions.
edit:
This works no problems:
val composed = ::isOdd compose add5 compose multiplyBy2
I am just trying to achieve the same result should I have a list of functions instead of writing this way.
I find it hard to imagine how a simple compose should work with methods having so different signatures. So first we would have to align the types of the functions. Arrow let's you compose functions if the return type of the first matches the parameter of the second...
Another problem is that isOdd is a Predicate. It is not transforming a value.
If the transformers have a compatible signature you can compose them with e.g. andThen
Here is a version that aligns the types to compose the functions. Note that filter and map are special functions in arrow's Option that allow you to pass transformer functions/predicates
import arrow.core.Option
import arrow.core.andThen
import org.hamcrest.Matchers.`is`
import org.junit.Assert.assertThat
import org.junit.Test
class ComposeTest {
#Test
fun shouldCompose() {
val add5 = { i: Int -> i + 5 }
val multiplyBy2 = { i: Int -> i * 2 }
val isOdd = { x: Int -> x % 2 != 0 }
val composed: (Int) -> Option<Int> = { i: Int -> Option.just(i)
.filter(isOdd)
.map(add5.andThen(multiplyBy2))
}
assertThat(composed(3), `is`(Option.just(16)))
assertThat(composed(4), `is`(Option.empty()))
}
}