Zobrazují se příspěvky se štítkemKotlin. Zobrazit všechny příspěvky
Zobrazují se příspěvky se štítkemKotlin. Zobrazit všechny příspěvky

neděle 5. ledna 2020

Kotlin Extendable Tests

Kotlin Extendable Tests

Kotlin Extendable Tests

Imagine, that you have a lot of System/Functional tests. And a lot of these test have some common code. E.g. One code clean your system data. Another starts your testing HttpServer. etc.

This code often end up in some class, lets call it “TestBase”, and all tests inherit from this TestBase. As time goes on this class is bigger and bigger. And a lot of tests, which doesn’t need clean system data are doing so, because this code is part of TestBase.

Solution is split this TestBase somehow. But Splitting it can be quite tricky.

In Groovy(link) this can be easyli done by using Traits (link). And each functionality became own Trait and only tests which need this functionality will implement this trait.

In Kotlin we do not have traits, but it is possilbe to use Interfaces and Delegation to easily implement something with similar capabilities.

Example

You can easily write tests, which use your extensions.

import ... Extendable
import ... ExtendableImpl
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import java.net.HttpURLConnection
import java.net.URL

class ExtensionSampleTest : Extendable by ExtendableImpl() {
    // Here is used our extenssion, which start and stop http server
    val server = register(HttpServerExtension())

    @Test
    fun `test using extension`() {
        // given
        val con = URL("http://localhost:${server.port}/test").openConnection() as HttpURLConnection
        con.setRequestMethod("GET")

        // when
        val response = String(con.inputStream.readAllBytes())

        // then
        assertThat(response).isEqualTo("This is the response")
    }

}

Extension which starts http server. Can look like this:

import com.sun.net.httpserver.HttpServer
import ... Extension
import java.net.InetSocketAddress


class HttpServerExtension(val port: Int = 8000) : Extension {

    lateinit var server: HttpServer

    override fun extBeforeEach() {
        start()
    }

    override fun extAfterEach() {
        stop()
    }

    private fun start() {
        server = HttpServer.create(InetSocketAddress(port), 0).apply {
            createContext("/test") { t ->
                val response = "This is the response"
                t.sendResponseHeaders(200, response.length.toLong())
                t.responseBody.apply {
                    write(response.toByteArray())
                    close()
                }
            }
            start()
        }
    }

    private fun stop() {
        server.stop(1)
    }
    
}

Implementation of extessions for jUnit5

  
import org.junit.jupiter.api.*  
import org.junit.jupiter.api.TestInstance.Lifecycle  
  
/**  
 * Be Aware! Implementing this Interface make the test @TestInstance(Lifecycle.PER_CLASS)
 * It means one test instance is used to run all tests!
 */
@TestInstance(Lifecycle.PER_CLASS)  
interface Extendable {  
  
    val extensions: MutableList<Extension>  
  
    fun <T : Extension> register(extension: T): T {  
        extensions.add(extension)  
        return extension  
    }  
  
    @BeforeAll  
    fun extendableBeforeAll() {  
        extensions.forEach(Extension::extBeforeAll)  
    }  
  
    @BeforeEach  
    fun extendableBeforeEach() {  
        extensions.forEach(Extension::extBeforeEach)  
    }  
  
    @AfterEach  
    fun extendableAfterEach() {  
        extensions.asReversed().forEach(Extension::extAfterEach)  
    }  
  
    @AfterAll  
    fun extendableAfterAll() {  
        extensions.asReversed().forEach(Extension::extAfterAll)  
    }  
}  
  
open class ExtendableImpl : Extendable {  
    override val extensions = mutableListOf<Extension>()  
}  
  
interface Extension {  
    fun extBeforeAll() {}  
    fun extBeforeEach() {}  
    fun extAfterEach() {}  
    fun extAfterAll() {}  
}

úterý 1. ledna 2019

Kotlin - Use spaces in name of tests.md

Kotlin - Use spaces in name of tests.md

Kotlin - Use spaces in name of tests

Kotlin allow us to use any characters in method names (even with spaces). If we enclose it in backticks.:
fun `method - name`() {}

It can be very convenient for name of tests. And I think, that we should definitely make use of this opportunity.

Then we can have descriptive names of tests like for example
Spock
have.

Example

We can figure out a lot of example, but will show you just one :)

Consider this simple test:

@Test  
fun `multiplyExact of zero integer should return zero`() {  
    //when  
    val result = Math.multiplyExact(10, 0)  
      
    //then  
    assertThat(result).isEqualTo(0)  
}

It is easier to understand name of test above, than following one:

@Test  
fun multiplyExactOfZeroIntegerShouldReturnZero() { ... }

Where ends the name of method and where starts test description?

Experience

I have never found any problem with method named like this.
IDE (IntelliJ IDEA) have no problem with methods named like this.
And jUnitRunner have no difficulties with this methods as well.

So there is no excuse for writing less readable test names.

pondělí 17. prosince 2018

Equivalent closures in Groovy and Kotlin

Equivalent closures in Groovy and Kotlin.md

Equivalent closures in Groovy and Kotlin

Closures in Groovy and Kotlin are very similar, but have different names. Here is is list with few of them.

Project with samples is here:
https://github.com/bugs84/samples/tree/master/kotlin-groovy-closures

Groovy Kotlin
each forEach
collect map
findAll filter
find find
groupBy groupBy
eachWithIndex forEachIndexed
with run, let
with :-/ apply, also

Note: Not every think is absolutely same, but it’s at least quite similar.

each vs. forEach

def sum = 0
[1, 2, 3].each { sum += it }
assert sum == 6
var sum = 0  
listOf(1, 2, 3).forEach { sum += it }  
assertThat(sum).isEqualTo(6)

collect vs. map

List<String> result = [1, 2, 3].collect { "S-" + it }  
assert result == ["S-1", "S-2", "S-3"]
val result = listOf(1, 2, 3).map { "S-" + it }  
assertThat(result).isEqualTo(listOf("S-1", "S-2", "S-3"))

findAll vs. filter

assert [1, 2, 3, 4, 5].findAll { it < 3 } == [1, 2]
assertThat(
        listOf(1, 2, 3, 4, 5).filter { it < 3 }
).isEqualTo(
        listOf(1, 2)
)

find vs. find

assert [1, 2, 3, 4, 5].find { it < 3 } == 1
assertThat(  
        listOf(1, 2, 3, 4, 5).find { it < 3 }  
).isEqualTo(
        1
)

groupBy vs. groupBy

Map<Integer, List<Integer>> groupBy = [1, 2, 3, 4, 5, 6, 7].groupBy { it % 3 }  
assert groupBy == [  
        0: [3, 6],  
        1: [1, 4, 7],  
        2: [2, 5]  
]
val groupBy: Map<Int, List<Int>> = listOf(1, 2, 3, 4, 5, 6, 7).groupBy { it % 3 }  
assertThat(groupBy).isEqualTo(
    mapOf(  
        0 to listOf(3, 6),  
        1 to listOf(1, 4, 7),  
        2 to listOf(2, 5)  
    ))

eachWithIndex vs. forEachIndexed

def result = ""  
["A", "B"].eachWithIndex { entry, index ->  
    result += "$index:$entry, "  
}  
assert result == "0:A, 1:B, "
var result = ""  
listOf("A", "B").forEachIndexed { index, entry ->  
  result += "$index:$entry, "  
}  
assertThat(result).isEqualTo("0:A, 1:B, ")

Note: entry and index are in different order

with vs. run

assert "string".with {  
    length()  
} == 6
assertThat(  
    "string".run {  
        length  
    }  
).isEqualTo(6)

with/run can be used just for for creating an scope:

assert with {  
    "AAA"  
} == "AAA"
assertThat(  
        run {  
            "AAA"  
        }  
).isEqualTo("AAA")

In Groovy with works as Kotlin let as well. See next example:

with vs. let

assert "string".with {  
    it.length()  
} == 6
assertThat(  
        "string".let {  
            it.length  
        }  
).isEqualTo(6)

with vs. apply

assert "string".with {  
    println length()  
    it //  :-/  
} == "string"
assertThat(  
        "string".apply {  
            println(length)  
        }
).isEqualTo("string")

with vs. also

assert "string".with {  
    println it.length()  
    it //  :-/  
} == "string"
assertThat(  
        "string".also {  
            println(it.length)  
        }  
).isEqualTo("string")

středa 9. května 2018

Kotlin - Append to StringBuilder using "+" "plus" instead of append() method

Kotlin - Override plus operator for StringBuilder

Kotlin - Append to StringBuilder using “+” “plus” instead of append() method

In Kotlin you can easily override operator. I will show simple example how can this help you to write more readable code.

At first we will override “plus” operator on for StringBuilder class

operator fun StringBuilder.plus(str: String): StringBuilder {
    append(str)
    return this
}

If you define this function as private. It will be accessible only in file, where it is defined. Or public and then it can be used anywhere in project.

And from this code:

sb.append("function ").append(generatedClassName).append("() {").append("\n")
functions.forEach { function: Function ->
    sb.append(indent).append("this.").append(function.name).append(" = undefined;").append("\n")
}
sb.append("}").append("\n")
sb.append("var ").append(name).append(" = new ").append(generatedClassName).append("();").append("\n")

we can remove all ‘append’ words and we get this code:
=>

sb + "function " + generatedClassName + "() {" + "\n"
functions.forEach { function: Function ->
    sb + indent + "this." + function.name + " = undefined;" + "\n"
}
sb + "}" + "\n"
sb + "var " + name + " = new " + generatedClassName + "();" + "\n"

Very simple, code is shorter and more readable.

pondělí 20. února 2017

Jak nadefinovat vlastní MBean ve Springu

Do svého aplikačního contextu přidat

    
    <context:mbean-export/>
(context je tento namespace xmlns:context="http://www.springframework.org/schema/context")

Pak již lze nadefinovat vlastní Mbean. (Příklad je v Kotlinu)

import org.springframework.jmx.export.annotation.ManagedOperation
import org.springframework.jmx.export.annotation.ManagedResource
import org.springframework.stereotype.Component

@Component
@ManagedResource(objectName = "MyApplicationName" + ":name=HelloMBeanWorld")
class HelloMBeanWorld {

    //Its component - you can simply autowire anything
    //@Autowired
    //private MyBean myBean

    @ManagedOperation(description = "My hello world MBean")
    fun getHelloMBeanWorld(): String {
        return "Hello MBean world!!!"
    }

}

A pak stačí spustit aplikaci a přistupovat ke své MBean třeba pomocí JConsole. Spring už pořeší, registrovaní a hlavně taky odregistrování bean a vše. Díky Springu!

Další možnosti jsou krásně popsané v dokumentaci springu: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/jmx.html