Why is the Result object wrapped in another Result? - kotlin

When using a ParameterizedTest annotation with a source containing an "Result" type class, the type of the parameter is not the same as the argument.
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.MethodSource
import java.util.stream.Stream
class testTest() {
companion object {
#JvmStatic
fun streamOfArguments(): Stream<Arguments?>? {
return Stream.of(
Arguments.of(Result.success(true)) // Type Result<Boolean>
)
}
}
#ParameterizedTest
#MethodSource("streamOfArguments")
fun testArgumentParameter(
argumentFromStream: Result<Result<Boolean>> // Wrapped in an additional successful `Result`
) {
println(argumentFromStream::class)
argumentFromStream.onSuccess{
println(it)
}
}
}

Related

Setters don't modify two-dimensional array

Kotlin. I want a button to display values from a two-dimensional ArrayList, and a second button to modify one of them. But the setters don't modify the two-dimensional ArrayList. We can see the values with the first button, and after modifying the values at index 2 (third) with the second button, the values don't change:
model.get(2).setDateStrs("03/03/20")
model.get(2).setHourStrs("10:27")
What's wrong?
ReModel.kt file:
package com.example.updatearraylist
class ReModel {
var dateStr:String = "12/31/2029"
var hourStr: String = "00:00"
fun getDateStrs(): String {
return dateStr
}
fun setDateStrs(dateStr: String) {
this.dateStr = dateStr
}
fun getHourStrs(): String {
return hourStr
}
fun setHourStrs(hourStr: String) {
this.hourStr = hourStr
}
}
MainActivity.kt file:
package com.example.updatearraylist
import android.R.attr
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import java.lang.reflect.Array.get
import java.util.*
import kotlin.collections.ArrayList
class MainActivity : AppCompatActivity() {
private var displayValueBtn: Button? = null
private var changeValueBtn: Button? = null
val model: ArrayList<ReModel>
get() {
val list = ArrayList<ReModel>()
for (i in 0..7) {
val model = ReModel()
model.setDateStrs("01/16/2020")
model.setHourStrs("01:08")
list.add(model)
}
return list
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
displayValueBtn =findViewById<Button>(R.id.displayValueBtn)
changeValueBtn=findViewById<Button>(R.id.changeValueBtn)
displayValueBtn!!.setOnClickListener {
for(i in 0..7){
Toast.makeText(this, "Value position "+i+" "+model.get(i).getDateStrs()+" "+
model.get(i).getHourStrs()
,Toast.LENGTH_SHORT).show()
}
}
changeValueBtn!!.setOnClickListener {
model.get(2).setDateStrs("03/03/20")
model.get(2).setHourStrs("10:27")
Toast.makeText(this,"List Modified",Toast.LENGTH_LONG).show()
}
}
}
The custom getter on model will be executed each time model is accessed so any changes to the array are overwritten. If you want to verify that use a single println in the custom getter and whatever you print will display multiple times.
By "custom getter" I mean the get() on model and the associated block of code.
One solution is to use lazy initialization instead of a custom getter so that model is initialized only once. Here's how that would look:
val model: ArrayList<ReModel> by lazy {
val list = ArrayList<ReModel>()
for (i in 0..7) {
val model = ReModel()
model.setDateStrs("01/16/2020")
model.setHourStrs("01:08")
list.add(model)
}
list
}
Note that the last line with just list on it returns the value of list. return is not allowed there.

How can I check Koin modules while using injection parameters?

I would like to check my configuration by using the checkModules() method provided by koin-test as explained here.
However, I am using injection parameters and my test fails with an exception:
org.koin.core.error.NoParameterFoundException: Can't get parameter value #0 from org.koin.core.parameter.DefinitionParameters#3804648a
Here is a simple test to demonstrate the issue:
import org.junit.Test
import org.koin.dsl.koinApplication
import org.koin.dsl.module
import org.koin.test.KoinTest
import org.koin.test.check.checkModules
class TestCase : KoinTest {
#Test
fun checkModules() {
koinApplication {
modules(
module { factory { (msg: String) -> Message(msg) } }
)
}.checkModules()
}
data class Message(val message: String)
}
Is there a way to make this work? How could I provide the missing parameter?
You need to pass this parameter to your test, like this:
class TestCase : KoinTest {
#Test
fun checkModules() {
koinApplication {
modules(module { factory { (msg: String) -> Message(msg) } })
}.checkModules {
create<Message> { parametersOf("testMessage") }
}
}
data class Message(val message: String)
}
Example from Koin repository: CheckModulesTest.kt#L156
My issue with the same question: Issue

kotlin-test: How to test for a specific type like: "is y instance of X"

How to test if a val/var is of an expected type?
Is there something I am missing in Kotlin Test, like:
value shouldBe instanceOf<ExpectedType>()
Here is how I implemented it:
inline fun <reified T> instanceOf(): Matcher<Any> {
return object : Matcher<Any> {
override fun test(value: Any) =
Result(value is T, "Expected an instance of type: ${T::class} \n Got: ${value::class}", "")
}
}
In KotlinTest, a lot is about proper spacing :)
You can use should to get access to a variety of built-in matchers.
import io.kotlintest.matchers.beInstanceOf
import io.kotlintest.should
value should beInstanceOf<Type>()
There is also an alternative syntax:
value.shouldBeInstanceOf<Type>()
See here for more information.
Since Kotlin 1.5 there is a nice solution included in kotlin-test:
assertIs<TypeExpected>(value)
This will not only assert that value is of type TypeExpected, it will also smart cast value, so that you can access all methods of TypeExpected. Just include the dependency, e.g:
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:1.7.10")
And you can do stuff like this
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
class AssertIsTests {
#Test
fun `test for list`() {
val list = Collector.List.collect(1, 1, 2, 2, 3)
assertEquals(5, list.size)
// assertEquals(1, list[0]) won't compile: list is of type Collection<Int> which doesn't support []
assertIs<List<Int>>(list) // from now on list is of type List<Int>!
assertEquals(4, list.indexOf(3)) // now there are all list methods
assertEquals(1, list[0]) // including indexed getters
}
#Test
fun `test for set`() {
val set = Collector.Set.collect(1, 1, 2, 2, 3)
assertEquals(3, set.size) // Set with duplicates removed
assertIs<Set<Int>>(set) // from now on set is of Type Set<Int>
}
}
enum class Collector {
List {
override fun collect(vararg args: Int) = args.toList()
},
Set {
override fun collect(vararg args: Int) = args.toSet()
};
abstract fun collect(vararg args: Int): Collection<Int>
}

Problem converting Java class containing a static factory method to Kotlin

I have had no sucess trying to convert this to Kotlin and use it.
It works as Java, just not with my Kotlin (which I converted using IntelliJ Kotlin Plugin)
the problem appears to be this part
#PluginFactory
public static AnsiConsoleAppender createAppender(
I tried to add #JvmStatic and I get this error:
Unable to invoke factory method in class AnsiConsoleAppender for element AnsiConsoleAppender: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method AnsiConsoleAppender$Companion.AnsiConsoleAppender, parameter filter java.lang.reflect.InvocationTargetException
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.appender.AppenderLoggingException;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
import java.io.Serializable;
/**
* <h2>AnsiConsoleAppender</h2>
*
* <h3>notes</h3>
* <li>class name need not match the #Plugin name</li>
* <h3>more<h3/>
* <li>How to Create a Custom Appender in log4j2?</li>
*/
#Plugin(name="AnsiConsoleAppender", category="Core", elementType="appender", printObject=true)
public final class AnsiConsoleAppender extends AbstractAppender {
protected AnsiConsoleAppender(String name, Filter filter,
Layout<? extends Serializable> layout, final boolean ignoreExceptions) {
super(name, filter, layout, ignoreExceptions);
}
// The append method is where the appender does the work.
// Given a log event, you are free to do with it what you want.
// This example demonstrates:
// 1. Concurrency: this method may be called by multiple threads concurrently
// 2. How to use layouts
// 3. Error handling
//#Override
public void append(LogEvent event) {
try {
final byte[] bytes = getLayout().toByteArray(event);
// output code goes here
} catch (Exception ex) {
if (!ignoreExceptions()) throw new AppenderLoggingException(ex);
}
}
// Your custom appender needs to declare a factory method
// annotated with `#PluginFactory`. Log4j will parse the configuration
// and call this factory method to construct an appender instance with
// the configured attributes.
#PluginFactory
public static AnsiConsoleAppender createAppender(
#PluginAttribute("name") String name,
#PluginElement("Layout") Layout<? extends Serializable> layout,
#PluginElement("Filter") final Filter filter,
#PluginAttribute("otherAttribute") String otherAttribute) {
if (name == null) {
LOGGER.error("No name provided for AnsiConsoleAppenderImpl");
return null;
}
if (layout == null) {
layout = PatternLayout.createDefaultLayout();
}
return new AnsiConsoleAppender(name, filter, layout, true);
}
}
import org.apache.logging.log4j.core.AbstractLifeCycle
import org.apache.logging.log4j.core.Filter
import org.apache.logging.log4j.core.Layout
import org.apache.logging.log4j.core.LogEvent
import org.apache.logging.log4j.core.appender.AbstractAppender
import org.apache.logging.log4j.core.appender.AppenderLoggingException
import org.apache.logging.log4j.core.config.plugins.Plugin
import org.apache.logging.log4j.core.config.plugins.PluginAttribute
import org.apache.logging.log4j.core.config.plugins.PluginElement
import org.apache.logging.log4j.core.config.plugins.PluginFactory
import org.apache.logging.log4j.core.layout.PatternLayout
import java.io.Serializable
#Plugin(name = "AnsiConsoleAppender", category = "Core", elementType = "appender", printObject = true)
class AnsiConsoleAppender /*protected*/ constructor(
name: String, filter: Filter,
layout: Layout<out Serializable>, ignoreExceptions: Boolean
) : AbstractAppender(name, filter, layout, ignoreExceptions) {
// The append method is where the appender does the work.
// Given a log event, you are free to do with it what you want.
// This example demonstrates:
// 1. Concurrency: this method may be called by multiple threads concurrently
// 2. How to use layouts
// 3. Error handling
//#Override
override fun append(event: LogEvent) {
try {
val bytes = layout.toByteArray(event)
//AnsiColor.out(String(bytes), ColorMaps.ASTERIKSY, null, true)
} catch (ex: Exception) {
if (!ignoreExceptions()) throw AppenderLoggingException(ex)
}
}
companion object {
// Your custom appender needs to declare a factory method
// annotated with `#PluginFactory`. Log4j will parse the configuration
// and call this factory method to construct an appender instance with
// the configured attributes.
#JvmStatic
#PluginFactory
fun createAppender(
#PluginAttribute("name") name: String?,
#PluginElement("Layout") layout: Layout<out Serializable>?,
#PluginElement("Filter") filter: Filter,
#PluginAttribute("otherAttribute") otherAttribute: String
): AnsiConsoleAppender? {
val lay = layout ?: PatternLayout.createDefaultLayout()
if (name == null) {
AbstractLifeCycle.LOGGER.error("No name provided for AnsiConsoleAppenderImpl")
return null
}
return AnsiConsoleAppender(name, filter, lay, true)
}
}
}
what am I missing?
The exception says filter is null.
So it should be nullable type.
companion object {
#PluginFactory
#JvmStatic
fun createAppender(
#PluginAttribute("name") name: String,
#PluginElement("Layout") layout: Layout<out Serializable>,
#PluginElement("Filter") filter: Filter?,
#PluginAttribute("otherAttribute") otherAttribute: String?
): AnsiConsoleAppender {
In Kotlin factory methods are usually defined in a companion object of a class, e.g.:
class AnsiConsoleAppender : AbstractAppender() {
companion object {
// add JvmStatic annotation if you want to call this method from Java file
#JvmStatic
#PluginFactory
fun createAppender(/*params*/): AnsiConsoleAppender {
//...
}
}
}
More on companion object

How mock Kotlin extension function in interface?

I have an extension function for interface like the following:
import javax.jms.ConnectionFactory
fun ConnectionFactory.foo() {
println("do some stuff")
}
How can I mock the function foo?
Please note, I have seen approaches for classes and objects in http://mockk.io/#extension-functions, but it does not work. I have tried this one:
import io.mockk.classMockk
import io.mockk.every
import org.junit.Test
import javax.jms.ConnectionFactory
class ExtensionFunctionTest {
#Test
fun mockExtensionFunction() {
val connectionFactory = classMockk(ConnectionFactory::class)
every { connectionFactory.foo() } returns println("do other stuff")
connectionFactory.foo()
}
}
It throws exception:
io.mockk.MockKException: Missing calls inside every { ... } block.
According to the documentation in case of module wide extension functions you need to staticMock "hidden" class created for an extension function.
Here is an example (assuming the file name is com/sample/extmockingtest/SampleTest.kt):
fun <T> Iterable<T>.foo(): String = "do some stuff"
class ExtensionFunctionTest {
#Test
fun mockExtensionFunction() {
val itMock = classMockk(Iterable::class);
staticMockk("com.sample.extmockingtest.SampleTestKt").use {
every {
itMock.foo()
} returns "do other stuff"
assertEquals("do other stuff", itMock.foo())
verify {
itMock.foo()
}
}
}
}