How to use spyk - kotlin

I need to verify that a method has been called on an object. So I make a spy of this object:
obj = spyk(obj)
And then I verify that a method has been called:
verify(exactly = 1) { obj.f(3) }
The test fails with the following error message:
java.lang.AssertionError: Verification failed: call 1 of 1: obj(#2).f(eq(3))) was not called.
However I can clearly see the method f being called:
I can break in that method in debug mode
I print out hello world from f() in that method and see it being printed.
How do I use spies correctly in mockk?
P.S.
I tried doing val obj = spyk(obj()) but I get lateinit has not been initialized error because I need to set a parameter in obj as so:
obj.setDependency(friend)
But in the case where I first do val obj = spyk(obj()) and then call obj.setDependency(friend) like I explained above I end up with a lateinit has not been initialized error
Can someone please help me resolve this issue?

In your case, I don't understand this part obj = spyk(obj). What are you doing here? I think this part don't even compile.
You receive lateinit has not been initialized error because spyk(obj()) calls real constructor. If your object has dependencies, you have to create them too or pass mockk instead of them.
According to the documentation:
Note: the spy object is a copy of a passed object.
You have to create this object like a normal object, so all dependencies have to be filled.
I am using spyk in this way, let me show you a quick example.
fun `should call method testMethod`() {
val spy = spyk<TestClass>()
spy.testMethod(1)
verify (exactly = 1) { spy.testMethod(1) }
}

Related

When does init block of object get called?

I tried to know when the init block of object gets called in Kotlin using the below code, but I don't get any result in the console:
fun main(args: Array<String>) {
TestObj
TestObj
}
object TestObj {
var count = 0
init {
fun howManyTimes() {
println(++count)
}
}
}
Dimitri's answer is correct for your problem, however the correct answer for your specific question is:
if it is a class instantiation, the init is executed before the constructor is called.
if it is an object, according to Kotlin documentation, it will be called whenever the (singleton) object is called first time, as static objects are lazy initialized.
You are not getting any output in console, because you are declaring function inside the init block, and not calling it.
Change TestObj code to:
object TestObj {
var count = 0
init {
howManyTimes()
}
fun howManyTimes() {
println(++count)
}
}
Above answer gives a clear explanation as to why you are not getting expected output, I would try to answer your question
When does init block of object get called?
From kotlin in action
The init keyword introduces an initializer block. Such blocks contain
initialization code that’s executed when the class is created, and are
intended to be used together with primary constructors. Because the
primary constructor has a constrained syntax, it can’t contain the
initialization code; that’s why you have initializer blocks. If you
want to, you can declare several initializer blocks in one class.

How can I initialize variable before each test using kotlin-test framework

I'm trying to find a way to set up variable before each test. Just like the #Before method in Junit. Go through the doc from kotlin-test, I found that I can use interceptTestCase() interface. But unfortunately, the code below will trigger exception:
kotlin.UninitializedPropertyAccessException: lateinit property text has not been initialized
class KotlinTest: StringSpec() {
lateinit var text:String
init {
"I hope variable is be initialized before each test" {
text shouldEqual "ABC"
}
"I hope variable is be initialized before each test 2" {
text shouldEqual "ABC"
}
}
override fun interceptTestCase(context: TestCaseContext, test: () -> Unit) {
println("interceptTestCase()")
this.text = "ABC"
test()
}
}
Am I in the wrong way to use interceptTestCase()?
Thank you very much~
A quick solution is to add below statement in test case:
override val oneInstancePerTest = false
The root cause is that oneInstancePerTest is true by default(although it's false in kotlin test doc), which means every test scenario will run in the different instances.
In the case in question,
The initializing interceptTestCase method ran in instance A, set text to ABC. Then the test case ran in instance B without interceptTestCase.
For more detail, there is an open issue in GitHub:
https://github.com/kotlintest/kotlintest/issues/174
You have not initialized the text variable.
init called first when you create an object for a class.
You are calling text shouldEqual "ABC" in init block in your code, that time there will be no value in a text variable.
Your function interceptTestCase(context: TestCaseContext, test: () -> Unit) only can be called after the init block.
Initialize the text at the constructor itself like below code, so you won't get this error or make some alternative.
class KotlinTest(private val text: String): StringSpec()

Why the variable can't be initialized correctly in inline function as in java?

We know the lambda body is lazily well, because if we don't call the lambda the code in the lambda body is never be called.
We also know in any function language that a variable can be used in a function/lambda even if it is not initialized, such as javascript, ruby, groovy and .etc, for example, the groovy code below can works fine:
def foo
def lambda = { foo }
foo = "bar"
println(lambda())
// ^--- return "bar"
We also know we can access an uninitialized variable if the catch-block has initialized the variable when an Exception is raised in try-block in Java, for example:
// v--- m is not initialized yet
int m;
try{ throw new RuntimeException(); } catch(Exception ex){ m = 2;}
System.out.println(m);// println 2
If the lambda is lazily, why does Kotlin can't use an uninitialized variable in lambda? I know Kotlin is a null-safety language, so the compiler will analyzing the code from top to bottom include the lambda body to make sure the variable is initialized. so the lambda body is not "lazily" at compile-time. for example:
var a:Int
val lambda = { a }// lambda is never be invoked
// ^--- a compile error thrown: variable is not initialized yet
a = 2
Q: But why the code below also can't be working? I don't understand it, since the variable is effectively-final in Java, if you want to change the variable value you must using an ObjectRef instead, and this test contradicts my previous conclusions:"lambda body is not lazily at compile-time" .for example:
var a:Int
run{ a = 2 }// a is initialized & inlined to callsite function
// v--- a compile error thrown: variable is not initialized yet
println(a)
So I only can think is that the compiler can't sure the element field in ObjectRef is whether initialized or not, but #hotkey has denied my thoughts. Why?
Q: why does Kotlin inline functions can't works fine even if I initializing the variable in catch-block like as in java? for example:
var a: Int
try {
run { a = 2 }
} catch(ex: Throwable) {
a = 3
}
// v--- Error: `a` is not initialized
println(a)
But, #hotkey has already mentioned that you should using try-catch expression in Kotlin to initializing a variable in his answer, for example:
var a: Int = try {
run { 2 }
} catch(ex: Throwable) {
3
}
// v--- println 2
println(a);
Q: If the actual thing is that, why I don't call the run directly? for example:
val a = run{2};
println(a);//println 2
However the code above can works fine in java, for example:
int a;
try {
a = 2;
} catch (Throwable ex) {
a = 3;
}
System.out.println(a); // println 2
Q: But why the code below also can't be working?
Because code can change. At the point where the lambda is defined the variable is not initialized so if the code is changed and the lambda is invoked directly afterwards it would be invalid. The kotlin compiler wants to make sure there is absolutely no way the uninitialized variable can be accessed before it is initialized, even by proxy.
Q: why does Kotlin inline functions can't works fine even if I initializing the variable in catch-block like as in java?
Because run is not special and the compiler can't know when the body is executed. If you consider the possibility of run not being executed then the compiler cannot guarentee that the variable will be initialized.
In the changed example it uses the try-catch expression to essentially execute a = run { 2 }, which is different from run { a = 2 } because a result is guaranteed by the return type.
Q: If the actual thing is that, why I doesn't call the run directly?
That is essentially what happens. Regarding the final Java code the fact is that Java does not follow the exact same rules of Kotlin and the same happens in reverse. Just because something is possible in Java does not mean it will be valid Kotlin.
You could make the variable lazy with the following...
val a: Int by lazy { 3 }
Obviously, you could use a function in place of the 3. But this allows the compiler to continue and guarantees that a is initialized before use.
Edit
Though the question seems to be "why can't it be done". I am in the same mind frame, that I don't see why not (within reason). I think the compiler has enough information to figure out that a lambda declaration is not a reference to any of the closure variables. So, I think it could show a different error when the lambda is used and the variables it references have not been initialized.
That said, here is what I would do if the compiler writers were to disagree with my assessment (or take too long to get around to the feature).
The following example shows a way to do a lazy local variable initialization (for version 1.1 and later)
import kotlin.reflect.*
//...
var a:Int by object {
private var backing : Int? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int =
backing ?: throw Exception("variable has not been initialized")
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
backing = value
}
}
var lambda = { a }
// ...
a = 3
println("a = ${lambda()}")
I used an anonymous object to show the guts of what's going on (and because lazy caused a compiler error). The object could be turned into function like lazy.
Now we are potentially back to a runtime exception if the programmer forgets to initialize the variable before it is referenced. But Kotlin did try at least to help us avoid that.

How do I initialize a final field in Kotlin?

Let's say I declared a final field with private final String s (Java) or val s (Kotlin). During initialization I want to initialize the field with the result of a call to a remote service. In Java I would be able to initialize it in the constructor (e.g. s = RemoteService.result()), but in Kotlin I can't figure out how to do that because as far as I can tell the field has to be initialized in the same line it's declared. What's the solution here?
You can set val value in init block:
class MyClass {
val s: String
init {
s = "value"
}
}
You can also initialize the value with by lazy the value will be initialized the first time it is referred. An example
val s by lazy { RemoteService.result() }
kotlin will guess the type of s from the return type of the expression.
You can use run:
class MyClazz {
val prop = run {
// do stuff
// do stuff again
123 // return expression
}
}
From the docs (emphasis is mine):
Besides calling run on a receiver object, you can use it as a non-extension function. Non-extension run lets you execute a block of several statements where an expression is required.
It has been possible to do it simply like this since the very first official stable release of Kotlin:
class MyClass {
val s = RemoteService.result()
}

What is the method signature when it uses an external param name in Swift

I'm using an NSTimer object in my Swift code, which requires a method signature to be passed to its 'selector' parameter in order to recurrently perform said method. When the method signature does not have an external parameter name i.e.
func timerMethod(internal: String) { ... }
I can pass the timer object this signature as so:
var timer = NSTimer.scheduledTimerWithTimeInterval(1.0,
target: self,
selector: Selector("timerMethod:"),
userInfo: userInfo,
repeats: true)
However, if I give the method a signature with an external parameter name, such as:
func timerMethod(external internal: String) { ... }
I can't figure out how to call the method. I attempted to log it using:
println("\(__FUNCTION__)")
Which logs the following:
timerMethod(external:)
But whenever I try this or any of the following, I receive 'unrecognized selector' exceptions:
timerMethod:
timerMethod:external
timerMethod:external:
timerMethod:(external)
timerMethod:(external:)
timerMethod(external):
Stumped for now. Anybody running into something similar?
It is timerMethodWithExternal: you can test that with object_getClass(t).instancesRespondToSelector(Selector("timerMethodWithExternal:"))
i used following code to introspect
func with(t: Test, inout count : CUnsignedInt) -> UnsafePointer<Method> {
var mc : CUnsignedInt = 0
return class_copyMethodList(object_getClass(t), &count)
}
var i=0
var mc : CUnsignedInt = 0
var t = Test()
var mlist = with(t,&mc)
var n : Int = Int(mc)
for (i=0; i<n;i++) {
println(sel_getName(method_getName(mlist[i])))
}
Although the signature for your method doesn't look correct, in Swift you pass a selector simply as the string name. The Apple documentation:
Because string literals can be automatically converted to selectors,
you can pass a string literal to any method that accepts a selector.
As for the signature of NSTimer.scheduledTimerWithTimeInterval, the Apple documentation states (See NSTimer documentation, Swift info):
The selector should have the following signature: timerFireMethod:
(including a colon to indicate that the method takes an argument). The
timer passes itself as the argument, thus the method would adopt the
following pattern:
func timerFireMethod(timer: NSTimer) { }
When you define your own method as:
func timerMethod(external internal: String) { ... }
then you are not meeting the required signature. The expected call is:
someMethod(timer: ...)
but your call would be:
someMethod(external: ...)
Besides, the argument should be of type NSTimer, not String. You were probably lucky that your first attempt worked.
FWIW, I ran into the same problem and was also able to confirm the proper selector name by inspecting the Xcode-generated header file in the derived data folder for my project.
Just search for your method's name ~/Library/Developer/Xcode/DerivedData and for the original poster's example you would find that the selector to be used is timerMethodWithExternal: (from its Objective-C definition).
I believe this header is generated even for pure Swift projects, the project I tested on had some Objective-C in it though.