Junit 5 Assertions.fail() can not infer type in Kotlin - kotlin

When I attempt to use JUnit 5 Assertions.fail in my Kotlin tests I get a compilation failure because parameter V can not be inferred:
import org.junit.jupiter.api.Assertions.fail
internal class MyTests {
#Test
fun simpleTest() {
fail("Does not compile")
}
}
Of course a simple solution to this problem is:
import org.junit.jupiter.api.Assertions.fail
internal class MyTests {
#Test
fun simpleTest() {
val result: Any = fail("Compiles")
}
}
However I do not wish to have to create an unused value in my code. Is there a way to define the type without having to declare a val? Also why does this happen in Kotlin? Java has no such problem with generics:
import org.junit.jupiter.api.Assertions.fail;
class MyJavaTests {
#Test
public void simpleTest() {
fail("Compiles);
}
}
EDIT: I discovered immediately after posting the question that the solution is to parameterize the call:
import org.junit.jupiter.api.Assertions.fail
internal class MyTests {
#Test
fun simpleTest() {
fail<Any>("Does not compile")
}
}
However still willing to accept an answer that can explain why I need to do this in kotlin.

Please see this issue: https://github.com/junit-team/junit5/issues/1209
It seems that this is already fixed in the junit-jupiter-api Assertions.kt file as a top-level function in org.junit.jupiter.api package.
Import the org.junit.jupiter.api.fail and not the org.junit.jupiter.api.Assertions.fail

Related

Serialize generic class using kotlinix.serialization in Kotlin/JS fails

Serializing a generic class with kotlinx.serialization succeeds in JVM but fails in JavaScript with message TypeError: tmp$.serializer is not a function. Please, see the following Unit Test.
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.encodeToString
import kotlin.test.Test
#Serializable
data class SerializationTest<T>(val myValue: T)
#Serializable
data class A(val st : SerializationTest<Int>)
class SerializationTests {
#Test
fun serializeWithKotlinx() {
// Succeeds in JVM but throws in JavaScript with "TypeError: tmp$.serializer is not a function"
Json.encodeToString(SerializationTest(3))
}
#Test
fun serializeWithKotlinxWithBox() {
// Succeeds always
Json.encodeToString(A(SerializationTest(3)))
}
}
How can I serialize a generic class in JavaScript?
See the docs here
Please note that this example works only on JVM because of serializer function restrictions. For JS and Native, explicit serializer should be used: format.encodeToString(PolymorphicSerializer(Project::class), data) You can keep track of this issue here.
In your case, this code:
Json.encodeToString(SerializationTest(3))`
uses generics, which is only available on JVM.
You'll have to manually pass the serializer to encodeToString(...)
#Test
fun serializeWithKotlinx() {
val encoded =
Json.encodeToString(
SerializationTest.serializer(Int.serializer()),
SerializationTest(3),
)
println(encoded)
}
Or use a SerializersModule (documented here):
#Test
fun serializeWithKotlinxSerializersModule() {
val module = SerializersModule {
contextual(SerializationTest::class) { args ->
SerializationTest.serializer(args[0])
}
}
val mapper = Json { serializersModule = module }
val encoded = mapper.encodeToString(
SerializationTest(3),
)
println(encoded)
}
As per this GitHub issue:
Unfortunately, this is a known problem and we do not support JS legacy anymore. Please use IR backend if possible
With JS IR it works well, giving the same results as JVM.

how to use Junit5 #TempDir with Kotlin ? ("JvmField can only be applied to final property" compile error)

I'm trying (without any luck so far) to use Junit5 #Tempdir annotation with Kotlin.
Following a previous stackoverflow post (link here), I've tried the code below:
#SpringBootTest
class MyClass {
#TempDir
#JvmField
var tempFolder: File? = null
#Test
fun mytest() {
assert(true);
}
}
Unfortunately I get the following error at compilation: "JvmField can only be applied to final property"...
Any idea ?
Thanks a lot in advance for your expertise and your time.
Best Regards
For other people still looking for an answer, below code works around above-mentionned issue:
#SpringBootTest
class MyClass {
#Test
fun mytest() {
assert(true);
}
companion object {
#TempDir
lateinit var tempFolder: File
}
}

How to mock an SQLiteOpenHelper

I am trying to mock an SQLiteOpenHelper class in instrumented tests so whenever any fragment tries to get information from the database it returns a generic result. However, I keep getting an error saying:
org.mockito.exceptions.base.MockitoException: Cannot mock/spy class
com.example.cleaningschedule.helpers.DatabaseHandler Mockito cannot
mock/spy because :
final class
at com.example.cleaningschedule.ToDoListInstrumentedTest.oneTask(ToDoListInstrumentedTest.kt:81)
The test class is:
#RunWith(AndroidJUnit4::class)
class ToDoListInstrumentedTest {
#Rule
#JvmField var activityRule: ActivityTestRule<MainActivity> = ActivityTestRule(MainActivity::class.java)
private fun getActivity() = activityRule.activity
#After
fun tearDown() {
InstrumentationRegistry.getInstrumentation().getTargetContext().deleteDatabase("TaskDatabase")
}
#Test
fun oneTask() {
val mock = mock(DatabaseHandler::class.java)
`when`(mock.getTasks()).thenThrow()
onView(withId(R.id.taskName)).check(matches(isDisplayed()))
}
}
The class I am trying to mock is:
class DatabaseHandler(context: Context): SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
companion object {
private const val DATABASE_VERSION = 5
private const val DATABASE_NAME = "TaskDatabase"
...
}
override fun onCreate(db: SQLiteDatabase?) {
...
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
...
}
fun getTasks(): MutableList<Pair<MutableList<String>, MutableList<Room>>> {
...
}
}
I have looked at several other similar questions but none have helped:
Error mocking Class which hold reference to SQLiteOpenHelper
Mock final class in java using mockito library - I had a lot of issues with import PowerMock
How to mock a final class with mockito - I have added the dependency and created the file with the mock-maker-inline line as suggested in the answers put I still get the same error. I also tried the answer that suggested Mockito.mock(SomeMockableType.class,AdditionalAnswers.delegatesTo(someInstanceThatIsNotMockableOrSpyable)) but this gave me a 'Not enough information to infer type variable T' error
Mock final class with Mockito 2
Mockito cannot mock/spy because : Final Class
Cannot mock/spy class java.util.Optional
I will made an Interface :
public interface ContainerHandler {
MutableList<Pair<MutableList<String>, MutableList<Room>>> getTasks();
}
Then I made DatabaseHandler inherit this interface, I call Mockito's mock function with the Interface.
val mock = mock(ContainerHandler::class.java)
`when`(mock.getTasks()).thenThrow()
And finally I inject my mock into the tested class.

How to write parametric/generic functions in kotlin

I'm trying to find a workaround to make a Spring Reactive Webclient work with JDBC.
Here is were I got the idea from: https://gitorko.github.io/2019/04/02/Spring-Webflux-Reactive-JDBC/.
I'm writing a service that calls the jdbc repository interface and instead of returning the type of my domain object MyClass returns a Mono<MyClass> like this:
//other specific imports here
import org.springframework.stereotype.Service
import reactor.core.publisher.Mono
import reactor.core.scheduler.Scheduler
import reactor.core.scheduler.Schedulers
import java.util.concurrent.Callable
#Service
class MyClassService(val repo: MyClassRepository, val jdbcScheduler: Scheduler){
fun save(obj: MyClass?): Mono<MyClass?>? {
return asyncCallable { repo.save(obj) }
}
protected fun <S> asyncCallable(callable: Callable<S>?): Mono<S>? {
return Mono.fromCallable(callable).subscribeOn(Schedulers.parallel()).publishOn(jdbcScheduler)
}
}
//this is a jdbc repository
interface MyClassRepository : CrudRepository<MyClass, UUID> {}
Now the problem is that calling asyncCallable { repo.save(obj) } returns the compile error inferred type is MyClass? but TypeVariable(S) was expected and Mono.fromCallable(callable).subscribeOn(Schedulers.parallel()).publishOn(jdbcScheduler) returns the compile error inferred type is Callable<S>? but Callable<out TypeVariable(T)!> was expected.
I understand by reading about kotlin generics that this has to do with the variance. If I'm not wrong the function asyncCallableis invariant on the generic type Sand in this case covariance is required?
I think the syntax you need is asyncCallable(Callable { repo.save(obj) }).
Complete example:
#Service
class MyClassService(val repo: MyClassRepository, val jdbcScheduler: Scheduler){
fun save(obj: MyClass): Mono<MyClass?>? {
return asyncCallable(Callable { repo.save(obj) })
}
protected fun <S> asyncCallable(callable: Callable<S>): Mono<S>? {
return Mono.fromCallable(callable).subscribeOn(Schedulers.parallel()).publishOn(jdbcScheduler)
}
}
I'd also remove the ?s, but I left them to keep it as close to your code as possible.

How do you Mockk a Kotlin top level function?

Mockk allows mocking static functions, but how does one mock a Kotlin top level function?
For example, if I have a Kotlin file called HelloWorld.kt, how do I mock the sayHello() function?
HelloWorld.kt
fun sayHello() = "Hello Kotlin!"
The following syntax has worked to me.
mockkStatic(::sayHello.javaMethod!!.declaringClass.kotlin)
I'm surprised there is nothing on the jvm-stdlib yet for this.
Edit:
This overload has now been introduced officially:
https://github.com/mockk/mockk/pull/518
mockkStatic(::sayHello)
There is way to mockk a top level function:
mockkStatic("pkg.FileKt")
every { fun() } returns 5
You just need to know which file this function goes. Check in JAR or stack trace.
To add on previous answers this is working:
mockkStatic("pkg.FileKt")
every { fun() } returns 5
Where mockStatic takes as an argument "package_name:class_file_name"
But to simplify the mockStatick call you can give your file a name for the compiler with #file:JvmName directly in the file.
HelloWorld.kt
#file:JvmName("hello")
fun sayHello() = "Hello Kotlin!"
HelloWorldTest.kt
mockkStatic("pkg.hello")
every { fun() } returns 5
More detailed explication on why this is necessary and other examples here:https://blog.kotlin-academy.com/mocking-is-not-rocket-science-mockk-advanced-features-42277e5983b5
Building on #Sergey's answer:
You could have the actual implementation of the sayHello() function in a variable that's then the default value of a function parameter to sayHello().
This example works:
package tests
import io.mockk.every
import io.mockk.mockk
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
val sayHelloKotlin = { "Hello Kotlin!" }
fun sayHello(producer: () -> String = sayHelloKotlin): String = producer()
class Tests {
interface Producer {
fun produce(): String
}
#Test
fun `Top level mocking`() {
val mock = mockk<Producer>()
every { mock.produce() } returns "Hello Mockk"
val actual = sayHello(mock::produce)
Assertions.assertEquals(actual, "Hello Mockk")
}
}
The problem with this is that you're changing production code just to cater for testing, and it feels contrived.
This code doesn't work for me with mockk version 1.10.0 but works well in 1.11.0 (of course need to change mockkStatic(::bar) )
Utils.kt
#file:JvmName("UtilsKt")
package com.example.myapplication
fun foo(): Boolean {
return bar()
}
fun bar():Boolean {
return false
}
Test
#RunWith(RobolectricTestRunner::class)
#Config(sdk = [Build.VERSION_CODES.O_MR1])
class ExampleUnitTest {
#Test
fun addition_isCorrect() {
mockkStatic("com.example.myapplication.UtilsKt")
every { bar() } returns true
assertTrue(foo())
}
}