I'm looking for help to debug/resolve the obscure error:
java.lang.IllegalArgumentException: wrong number of arguments
I don't want to get into an argument of whether or not I should be testing a private method. But, I'm open to learning how to restructure this into something more testable that isn't exposed to the plugin user. I just can't seem to make it work. Below is the signature of the method and the relevant code.
Output from println in test setup
public static final org.gradle.api.file.DirectoryProperty my.project.MyPlugin.access$getResolvedDir(my.project.MyPlugin,java.lang.Object,org.gradle.api.Project)
Class under test
class MyPlugin : Plugin<Project> {
override fun apply(project: Project): Unit = project.run {
// do some gradle plugin stuff
myTask.configure { dir = getResolvedDir(pluginExtension.dir, project) }
}
private fun getResolvedDir(dir: Any?, project: Project): DirectoryProperty {
// do some stuff to transform the input to a DirectoryProperty
return resolvedDir
}
}
Spock test case
class MyPluginTest extends Specification {
#Rule
public final TemporaryFolder testProjectDir = new TemporaryFolder()
private Project p
private MyPlugin plugin
private CachedMethod getResolvedDirMethod
def setup() {
p = ProjectBuilder.builder().withName("install-plugin-test").build()
plugin = new InstallPlugin()
plugin.metaClass.methods.each {
if (it.name.contains("getResolvedDir"))
getResolvedDirMethod = it
}
getResolvedDirMethod.setAccessible()
println getResolvedDirMethod
}
def "String resolution for dir"() {
when:
def x = "xyz"
then:
DirectoryProperty dir = getResolvedDirMethod.invoke(plugin, x, p)
}
}
Related
I may use case the class under test has many cases so it is divided into a structure of inner classes. I want to write parameterized test cases to reduce boiler plate and code duplication.
For this I wanted to go with the approach of method source.
Class under test
class UnderTest
{
testThisMethod(a:String,b:String?){
// Do something
externalInterface.call(a?:b)
}
}
Test Case structure
internal class A() {
private val externalService = mockk<ExternalService>
private val test: UnderTest(externalService)
// Some general tests
//---Position Outter
inner class A {
//--- Position A
inner class B {
//--- Position C
#ParameterizedTest
#MethodSource("provideArguments")
fun `with arguments external service create object`(
argument1: String,
argument2: String,
expected: String
) {
// Some code
verify {
externalService.call(expected)
//some more verification
}
}
}
}
}
To provide the argument provider method I tried placing it at positions and got following errors
Position outer: initialization error :Could not find factory method in class
Position A,B: compilation error: companion not allowed here
How can this be achieved?
Try using #TestInstance(TestInstance.Lifecycle.PER_CLASS)
internal class A() {
private val externalService = mockk
private val test: UnderTest(externalService)
// Some general tests
//---Position Outter
inner class A {
//--- Position A
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
inner class B {
//--- Position C
#ParameterizedTest
#MethodSource("provideArguments")
fun `with arguments external service create object`(
argument1: String,
argument2: String,
expected: String
) {
// Some code
verify {
externalService.call(expected)
//some more verification
}
}
}
}
}
Check here for more details
Is it possible to access extension functions from Java code?
I defined the extension function in a Kotlin file.
package com.test.extensions
import com.test.model.MyModel
/**
*
*/
public fun MyModel.bar(): Int {
return this.name.length()
}
Where MyModel is a (generated) java class. Now, I wanted to access it in my normal java code:
MyModel model = new MyModel();
model.bar();
However, that doesn't work. The IDE won't recognize the bar() method and compilation fails.
What does work is using with a static function from kotlin:
public fun bar(): Int {
return 2*2
}
by using import com.test.extensions.ExtensionsPackage so my IDE seems to be configured correctly.
I searched through the whole Java-interop file from the kotlin docs and also googled a lot, but I couldn't find it.
What am I doing wrong? Is this even possible?
Perhaps like this:
// CallExtensionFunction.java
package com.example.groundup;
public class CallExtensionFunction {
public static void main(String[] args) {
MyModel myModel = new MyModel();
int bar = MyModelKt.bar(myModel);
System.out.println(bar);
}
}
// MyModell.kt
package com.example.groundup
fun MyModel.bar(): Int {
return this.name.length
}
class MyModel() {
val name = "Hugo"
}
The extension function is provided in the corresponding singleton with the suffix "Kt" as a static method.
I can access a private val value using reflection as below
fun main() {
val mainClass = MainClass()
val f = MainClass::class.memberProperties.find { it.name == "info" }
f?.let {
it.isAccessible = true
val w = it.get(mainClass) as String
println(w)
}
}
class MainClass {
private val info: String = "Hello"
}
But if I want to change info, how could I do it using reflection?
Answer
In short, you have to use Java reflection APIs in this case, and here is how to do it:
fun main() {
val mainClass = MainClass()
val f = MainClass::class.java.getDeclaredField("info")
f.isAccessible = true
f.set(mainClass, "set from reflection")
mainClass.printInfo() // Prints "set from reflection"
}
class MainClass {
private val info: String = "Hello"
fun printInfo() = println(info)
}
Reason for using Java reflection APIs
It is not possible to do with Kotlin reflection APIs since no setter code is generated for a read-only (val) property. So to change it, we need to use Java reflection APIs which is more low-level. First, we use Tools -> Kotlin -> Show Kotlin Bytecode to see what the generated bytecode looks like. Then we see this:
// ================MainClass.class =================
// class version 50.0 (50)
// access flags 0x31
public final class MainClass {
// access flags 0x12
private final Ljava/lang/String; info = "Hello"
// ...
i.e that the info fields in the MainClass Kotlin class causes the compiler to emit JVM code for a regular MainClass Java class with a final String info field. So to change it, we can use Java reflection APIs, as in the code above.
Kotlin reflection API attempt
If the field would have been private var you would be able to Use Kotlin reflection APIs like this:
f?.let {
val mutableProp = it as KMutableProperty<*>
it.isAccessible = true
mutableProp.setter.call(mainClass, "set from Kotlin reflection")
val w = it.get(mainClass) as String
println(w)
}
but if you try this with private val you will get the below exception
Exception in thread "main" java.lang.ClassCastException: class kotlin.reflect.jvm.internal.KProperty1Impl cannot be cast to class kotlin.reflect.KMutableProperty (kotlin.reflect.jvm.internal.KProperty1Impl and kotlin.reflect.KMutableProperty are in unnamed module of loader 'app')
at MainKt.main(main.kt:107)
at MainKt.main(main.kt)
since no setter code is generated for val fields, and thus the info property will have a Kotlin Reflection API type of KProperty and not KMutableProperty.
This is working solution
import kotlin.reflect.KMutableProperty
import kotlin.reflect.full.memberProperties
class MySolution {
var name = ""
var email = ""
}
#Suppress("UNCHECKED_CAST")
fun main() {
//Dummy Result Set
val rs = mapOf<String,String>("name" to "My Name", "email" to "My Email")
val mySol = MySolution();
val xyzMp = MySolution::class.memberProperties;
xyzMp.forEach { mp ->
val prop = mp as KMutableProperty<String>
prop.setter.call(mySol, rs[mp.name])
}
println(mySol.name)
println(mySol.email)
println("*****Enjoy*******")
output
My Name
My Email
**** *Enjoy*******
I am trying to test my AkkaHTTP routes (written in Kotlin) using akka-http-testkit. The tests in our project use Spek and I would like to keep it this way.
The Route TestKit tutorial gives a Java example:
public class TestkitExampleTest extends JUnitRouteTest {
TestRoute appRoute = testRoute(new MyAppService().createRoute())
#Test
public void testCalculatorAdd() {
// test happy path
appRoute.run(HttpRequest.GET("/calculator/add?x=4.2&y=2.3"))
.assertStatusCode(200)
.assertEntity("x + y = 6.5")
// test responses to potential errors
appRoute.run(HttpRequest.GET("/calculator/add?x=3.2"))
.assertStatusCode(StatusCodes.NOT_FOUND) // 404
.assertEntity("Request is missing required query parameter 'y'")
// test responses to potential errors
appRoute.run(HttpRequest.GET("/calculator/add?x=3.2&y=three"))
.assertStatusCode(StatusCodes.BAD_REQUEST)
.assertEntity("The query parameter 'y' was malformed:\n" +
"'three' is not a valid 64-bit floating point value")
}
}
The setup uses the testRoute function, which means the test class must extend JUnitRouteTest.
Attempting to translate to a Kotlin Spek test I got this:
class TestKitExampleTest : JUnitRouteTest(), Spek({
describe("My routes") {
val appRoute = testRoute(MyAppService().createRoute())
it("calculator add") {
// test happy path
appRoute.run(HttpRequest.GET("/calculator/add?x=4.2&y=2.3"))
.assertStatusCode(200)
.assertEntity("x + y = 6.5")
//...rest omitted
}
}
})
which does not compile as the class is attempting to inherit two classes. I converted it to the following instead:
class TestKitExampleTest : Spek({
describe("My routes") {
val appRoute = testRoute(MyAppService().createRoute())
it("calculator add") {
// test happy path
appRoute.run(HttpRequest.GET("/calculator/add?x=4.2&y=2.3"))
.assertStatusCode(200)
.assertEntity("x + y = 6.5")
//...rest omitted
}
}
}) {
companion object : JUnitRouteTest()
}
which encouters the runtime error java.lang.IllegalStateException: Unknown factory null
at akka.http.impl.util.package$.actorSystem(package.scala:34).
Is there a way to use Akka's route testkit with Spek? Or is there another way to test these routes?
As #raniejade mentioned above, answered on Github. JUnitRouteTest bootstraps Akka with a rule, but Spek's LifeCycleListener can do the same thing.
Adding the code:
class SpekRouteBootstrapper: LifecycleListener, JUnitRouteTest() {
override fun beforeExecuteTest(test: TestScope) {
systemResource().before()
}
override fun afterExecuteTest(test: TestScope) {
systemResource().after()
}
}
allowed me to do this on the test class:
class TestKitExampleTest: Spek({
val bootstrapper = SpekRouteBootstrapper()
registerListener(bootstrapper)
describe("My routes") {
val appRoute by memoized {
bootstrapper.testRoute(MyAppService().createRoute())
}
it("calculator add") {
// test happy path
appRoute.run(HttpRequest.GET("/calculator/add?x=4.2&y=2.3"))
.assertStatusCode(200)
.assertEntity("x + y = 6.5")
}
}
})
This code sample cannot be compiled and shows an internal error.
open class TestClass {
open inner class Back {
open fun dd() { }
}
}
class Manager: TestClass() {
private val test = object : Back() {
override fun dd() { }
}
}
Cause:
Error generating constructors of class null with kind IMPLEMENTATION
What does it mean?
The example provided refers to KT-11833 and now compiles. Checked it with Kotlin version 1.1.0-beta-22.