κ·Έ λ°μ μ½νλ¦° κΈ°λ₯
μ€νλ§ νλ μμν¬
μ½λ£¨ν΄κ³Ό ꡬ쑰μ λμμ±
ν
μ€νΈ
ν
μ€νΈ ν΄λμ€ μλͺ
μ£ΌκΈ° μ€μ
Junit 5μ ν
μ€νΈ μλͺ
μ£ΌκΈ°λ₯Ό κΈ°λ³Έκ°μΈ ν
μ€νΈ ν¨μλΉ ν λ² λμ
ν΄λμ€ μΈμ€ν΄μ€λΉ ν λ²μ© μΈμ€ν΄μ€ν νκ³ μΆλ€λ©΄,
@TestInstance
μ λ
Έν
μ΄μ
μ΄λ junit-platform.properties νμΌμ lifecycle.default
μμ±μ μ€μ νμ.
Junit 5λ ν΄λμ€μμ @TestInstance
μ λ
Έν
μ΄μ
μ μ¬μ©ν΄ μλͺ
μ£ΌκΈ°λ₯Ό λͺ
μν μ μλ€.
ν
μ€νΈ μΈμ€ν΄μ€μ μλͺ
μ£ΌκΈ°λ₯Ό PER_CLASSλ‘ μ€μ νλ©΄ ν
μ€νΈ ν¨μμ μκ³Ό μκ΄ μμ΄ ν
μ€νΈ μΈμ€ν΄μ€κ° λ± νλλ§ μμ±
Copy @TestInstance(TestInstance.Lifecycle.PER_CLASS) // λͺ¨λ ν
μ€νΈμ ν
μ€νΈ ν΄λμ€ μΈμ€ν΄μ€κ° μ€μ§ νλ
class JUnit5ListTests {
// μΈμ€ν΄μ€νμ μμ μ΄κΈ°νκ° μ€μ§ νλ²
private val strings = listOf("this", "is", "a", "list", "of", "strings")
// ν
μ€νΈ ν λλ§λ€ μ€ν μ λ€μ μ΄κΈ°ν
private lateinit var modifiable : MutableList<Int>
@BeforeEach
fun setUp() {
// ν
μ€νΈ ν λλ§λ€ μ€ν μ λ€μ μ΄κΈ°ν
modifiable = mutableListOf(3, 1, 4, 1, 5)
println("Before: $modifiable")
}
@AfterEach
fun finish() {
println("After: $modifiable")
}
@Test
fun addElementsToList() {
modifiable.add(9)
modifiable.add(2)
modifiable.add(6)
modifiable.add(5)
assertEquals(9, modifiable.size)
}
@Test
fun size() {
println("Testing size")
assertEquals(6, strings.size)
assertEquals(5, modifiable.size)
}
@Test
fun accessBeyondEndThrowsException() {
println("Testing out of bounds exception")
assertThrows<ArrayIndexOutOfBoundsException> { strings[99] }
assertEquals(6, strings.size)
}
}
@TestInstance
λ₯Ό κ° ν
μ€νΈ ν΄λμ€μ λ°λ³΅νλ λμ λͺ¨λ ν
μ€νΈμ μλͺ
μ£ΌκΈ°λ₯Ό properties νμΌμ μ€μ κ°λ₯
ν΄λμ€ ν¨μ€(src/test/resource)μ junit-platform.properties νμΌμ μμ±νμ¬ ν
μ€νΈ μλͺ
μ£ΌκΈ° μ€μ κ°λ₯
Copy junit.jupiter.testinstance.lifecycle.default = per_class
ν
μ€νΈμ λ°μ΄ν° ν΄λμ€ μ¬μ©νκΈ°
μνλ λͺ¨λ μμ±μ μΊ‘μννλ λ°μ΄ν° ν΄λμ€ μμ±νκΈ°
assertAll
ν¨μμ μ₯μ μ Executable μΈμ€ν΄μ€μ λ¨μΈμ΄ 1κ° μ΄μ μ€ν¨νλλΌλ Executable μΈμ€ν΄μ€λ₯Ό λͺ¨λ μ€ν
μ½νλ¦° data ν΄λμ€μλ μ΄λ―Έ equals λ©μλκ° μ¬λ°λ₯΄κ² ꡬνλμ΄ μμΌλ―λ‘ ν
μ€νΈ νλ‘μΈμ€ κ°μν
λ¨ νλμ assertionμ΄ λͺ¨λ μμ±μ ν
μ€νΈ
assertjμμ μ 곡νλ contains λ©μλλ₯Ό μ΄μ©ν΄ 컬λ μ
μ λͺ¨λ μμλ₯Ό νμΈν μ μλ€.
Copy data class Book(
val isbn: String,
val title: String,
val author: String,
val published: LocalDate
)
...
@Test
fun `check all elements in list`() {
val badBook = books[2].copy(title = "Modern Java Cookbook")
val found = arrayOf(books[2], books[0], books[1]) // Add badBook to see the assertion fail
val expected = books
assertThat(found).contains(*expected)
}
κΈ°λ³Έ μΈμμ ν¨κ» λμ ν¨μ μ¬μ©νκΈ°
κ° μΈμμ κΈ°λ³Έκ°μ μ 곡νλλ‘ ν΄λμ€λ₯Ό μμ νμ§ λ§κ³
κΈ°λ³Έκ°μ μμ±νλ ν©ν 리 ν¨μλ₯Ό μΆκ°νμ.
μ΅μμ λ 벨μ μ νΈλ¦¬ν° ν΄λμ€μ ν©ν 리 ν¨μλ₯Ό μμΉμν€λ©΄ ν
μ€νΈμμ ν©ν 리 ν¨μλ₯Ό μ¬νμ©ν μ μλ€.
Copy fun createBook(
isbn: String = "149197317X",
title: String = "Modern Java Recipes",
author: String = "Ken Kousen",
published: LocalDate = LocalDate.parse("2017-08-26")
) = Book(isbn, title, author, published)
val mjr = createBook()
...
data class MultiAuthorBook(
val isbn: String,
val title: String,
val authors: List<String>,
val published: LocalDate
)
fun createMultiAuthorBook(
isbn: String = "9781617293290",
title: String = "Kotlin in Action",
authors: List<String> = listOf("Dimitry Jeremov", "Svetlana Isakova"),
published: LocalDate = LocalDate.parse("2017-08-26")
) = MultiAuthorBook(isbn, title, authors, published)
μ¬λ¬ λ°μ΄ν°μ JUnit 5 ν
μ€νΈ λ°λ³΅νκΈ°
Junit 5μλ μΌνλ‘ κ΅¬λΆλ κ°(CSV)κ³Ό ν©ν 리 λ©μλκ° ν¬ν¨λ μ΅μ
κ³Ό ν¨κ» λ°μ΄ν° μμ€λ₯Ό λͺ
μν μ μλ νλΌλ―Έν°νλ ν
μ€νΈκ° μλ€.
ππ» CSV λ°μ΄ν°λ₯Ό μ¬μ©ν΄ ν리미ν°νλ ν
μ€νΈ μν
Copy @ParameterizedTest
@CsvSource("1, 1", "2, 1", "3, 2",
"4, 3", "5, 5", "6, 8", "7, 13",
"8, 21", "9, 34", "10, 55")
fun `first 10 Fibonacci numbers (csv)`(n: Int, fib: Int) =
assertThat(fibonacci(n)).isEqualTo(fib)
Junit 5μμλ ν©ν 리 λ©μλλ₯Ό μ¬μ©ν΄ ν
μ€νΈ λ°μ΄ν°λ₯Ό μμ±ν μ μλ€.
ππ» νλΌλ―Έν° μμ€λ‘μ μΈμ€ν΄μ€ ν¨μμ μ κ·Ό
Copy private fun fibnumbers() = listOf(
Arguments.of(1, 1), Arguments.of(2, 1),
Arguments.of(3, 2), Arguments.of(4, 3),
Arguments.of(5, 5), Arguments.of(6, 8),
Arguments.of(7, 13), Arguments.of(8, 21),
Arguments.of(9, 34), Arguments.of(10, 55))
@ParameterizedTest(name = "fibonacci({0}) == {1}")
@MethodSource("fibnumbers")
fun `first 10 Fibonacci numbers (instance method)`(n: Int, fib: Int) =
assertThat(fibonacci(n)).isEqualTo(fib)
ν
μ€νΈ μλͺ
μ£ΌκΈ°κ° κΈ°λ³Έ μ΅μ
μΈ Lifecycle.PER_METHOD λΌλ©΄ ν
μ€νΈ λ°μ΄ν° μμ€ ν¨μλ₯Ό λλ° κ°μ²΄ μμ μμΉμμΌμΌ νλ€.
Copy companion object {
// needed if parameterized test done with Lifecycle.PER_METHOD
@JvmStatic
fun fibs() = listOf(
Arguments.of(1, 1), Arguments.of(2, 1),
Arguments.of(3, 2), Arguments.of(4, 3),
Arguments.of(5, 5), Arguments.of(6, 8),
Arguments.of(7, 13), Arguments.of(8, 21),
Arguments.of(9, 34), Arguments.of(10, 55))
}
@ParameterizedTest(name = "fibonacci({0}) == {1}")
@MethodSource("fibs")
fun `first 10 Fibonacci numbers (companion method)`(n: Int, fib: Int) =
assertThat(fibonacci(n)).isEqualTo(fib)
νλΌλ―Έν°νλ ν
μ€νΈμ data ν΄λμ€ μ¬μ©νκΈ°
μ
λ ₯ κ°κ³Ό μμ κ°μ κ°μΈλ data ν΄λμ€λ₯Ό λ§λ€κ³
λ§λ data ν΄λμ€ κΈ°λ°μ ν
μ€νΈ λ°μ΄ν°λ₯Ό μμ±νλ ν¨μλ₯Ό ν
μ€νΈ λ©μλ μμ€λ‘μ μ¬μ©νμ.
Copy @JvmOverloads
tailrec fun fibonacci(n: Int, a: Int = 0, b: Int = 1): Int =
when (n) {
0 -> a
1 -> b
else -> fibonacci(n - 1, b, a + b)
}
ππ» μ
λ ₯κ³Ό μμλλ μΆλ ₯μ λ΄λ λ°μ΄ν° ν΄λμ€ μ μ
λ°μ΄ν° ν΄λμ€λ μ΄λ―Έ toStringμ΄ μ¬μ μλμ΄ μμΌλ―λ‘
μ
λ ₯κ³Ό μΆλ ₯ μμ λνλ΄λ data ν΄λμ€λ₯Ό μΈμ€ν΄μ€ννλ νλΌλ―Έν°νλ ν
μ€νΈλ₯Ό μ¬μ©νλ ν
μ€νΈ λ©μλλ₯Ό μμ±ν μ μλ€.
Copy data class FibonacciTestData(val number: Int, val expected: Int)
...
@ParameterizedTest
@MethodSource("fibonacciTestData")
fun `check fibonacci using data class`(data: FibonacciTestData) {
assertThat(fibonacci(data.number)).isEqualTo(data.expected)
}
private fun fibonacciTestData() = Stream.of(
FibonacciTestData(number = 1, expected = 1),
FibonacciTestData(number = 2, expected = 1),
FibonacciTestData(number = 3, expected = 2),
FibonacciTestData(number = 4, expected = 3),
FibonacciTestData(number = 5, expected = 5),
FibonacciTestData(number = 6, expected = 8),
FibonacciTestData(number = 7, expected = 13)
)
μ
λ ₯/μΆλ ₯
useλ‘ λ¦¬μμ€ κ΄λ¦¬νκΈ°
νμ€νκ² λ¦¬μμ€λ₯Ό λ«κ³ μΆμ§λ§ μ½νλ¦°μ μλ°μ try-with-resource ꡬ문μ μ§μνμ§ μλλ€.
kotlin.io ν¨ν€μ§μ use λλ java.io.Readerμ useLines
νμ₯ ν¨μλ₯Ό μ¬μ©νμ.
File.useLines
useLinesμ 첫 λ²μ§Έ μ νμ μΈμλ λ¬Έμ μ§ν©μ΄λ©° κΈ°λ³Έκ°μ UTF-8
λ λ²μ§Έ μΈμλ νμΌμ μ€μ λνλ΄λ Sequenceλ₯Ό μ λ€λ¦ μΈμ Tλ‘ λ§€ννλ λλ€
Copy public inline fun <T> File.useLines(
charset: Charset = Charsets.UTF_8,
block: (Sequence<String>) -> T
): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
// BufferedReaderλ₯Ό μμ±νκ³ BufferedReaderμ use ν¨μμ μ²λ¦¬λ₯Ό μμ
return bufferedReader(charset).use { block(it.lineSequence()) }
}
...
fun get10LongestWordsInDictionary() =
File("/usr/share/dict/words").useLines { line ->
line.filter { it.length > 20 }
.sortedByDescending(String::length)
.take(10)
.toList()
}
@Test @EnabledOnOs(OS.MAC)
internal fun `10 longest words in dictionary`() {
get10LongestWordsInDictionary().forEach { word -> println("$word (${word.length})") }
}
use ν¨μμ μκ·Έλμ²
Copy public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R
νμΌμ κΈ°λ‘νκΈ°
File ν΄λμ€μ νμ₯ ν¨μμλ μΌλ°μ μΈ μλ° μ
μΆλ ₯ λ©μλ λΏλ§ μλλΌ μΆλ ₯ μ€νΈλ¦Ό κ³Ό Writerλ₯Ό 리ν΄νλ νμ₯ ν¨μ κ° μλ€.
forEachLine
ν¨μλ₯Ό μ¬μ©ν΄μ νμΌμ μνν μ μλ€.
νμΌμ΄ λ§€μ° ν¬μ§ μλ€λ©΄ Fileμ readLines
λ₯Ό νΈμΆν΄ λͺ¨λ μ€μ΄ λ΄κΈ΄ 컬λ μ
μ νλν μλ μλ€.
useLines
ν¨μλ₯Ό μ¬μ©ν΄ νμΌμ μ€λ§λ€ νΈμΆλλ ν¨μλ₯Ό μ 곡ν μ μλ€.
νμΌμ ν¬κΈ°κ° μλ€λ©΄ readText
λλ readBytes
λ₯Ό μ¬μ©ν΄ μ 체 λ΄μ©μ κ°κ° λ¬Έμμ΄μ΄λ λ°μ΄νΈ λ°°μ΄λ‘ μ½μ΄μ¬ μ μλ€.
νμΌμ μ‘΄μ¬νλ λ΄μ©μ λͺ¨λ κ΅μ²΄νκ³ μΆλ€λ©΄ writeText
ν¨μλ₯Ό μ¬μ©
Copy File("myfile.txt).writeText("My data")
File ν΄λμ€μλ νμΌμ λ°μ΄ν°λ₯Ό μΆκ°νλ appendText
λΌλ νμ₯ ν¨μκ° μλ€.
writeText
μ appendText
ν¨μλ writeBytes
μ appendBytes
μ κΈ°λ‘ μμ
μ μμνλ€.
writeBytes
μ appendBytes
λ κΈ°λ‘μ΄ λλλ©΄ use
ν¨μλ₯Ό μ¬μ©ν΄ νμΌμ νμ€ν λ«λλ€.
κ°λ°μλ OutputStreamWriter
μ BufferedWriter
λ₯Ό 리ν΄νλ writer
(printWriter)μ bufferedWriter
ν¨μλ₯Ό μ¬μ©ν μλ μλ€.
Copy File(fileName).printWriter().use { writer ->
writer.println(data) }
κ·Έ λ°μ μ½νλ¦° κΈ°λ₯
μ½νλ¦° λ²μ μμλ΄κΈ°
μ½νΈλ₯Ό μμ±ν΄ νμ¬ μ¬μ© μ€μΈ μ½νλ¦° λ²μ μ μλ €λ©΄,
KotlinVersion ν΄λμ€ λλ° κ°μ²΄μ CURRENT μμ±μ μ¬μ©νμ.
ππ» μ½νλ¦° λ²μ λΉκ΅νκΈ°
Copy @Test
internal fun `comparison of KotlinVersion instances work`() {
val v12 = KotlinVersion(major = 1, minor = 2)
val v1341 = KotlinVersion(1, 3, 41)
assertAll(
{ assertTrue(v12 < KotlinVersion.CURRENT) },
{ assertTrue(v1341 <= KotlinVersion.CURRENT) },
{ assertEquals(KotlinVersion(1, 3, 41),
KotlinVersion(major = 1, minor = 3, patch = 41)) }
)
}
@Test
internal fun `current version is at least 1_3`() {
assertTrue(KotlinVersion.CURRENT.isAtLeast(major = 1, minor = 3))
assertTrue(KotlinVersion.CURRENT.isAtLeast(major = 1, minor = 3, patch = 40))
}
λ°λ³΅μ μΌλ‘ λλ€ μ€ννκΈ°
μ£Όμ΄μ§ λλ€ μμ μ¬λ¬ λ² μ€ννκ³ μΆλ€λ©΄ μ½νλ¦° λ΄μ₯ repeat ν¨μλ₯Ό μ¬μ©νμ.
Copy /**
* times: λ°λ³΅ν νμ
* action: μ€νν (Int) -> Unit νμμ ν¨μ
*/
@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
contract { callsInPlace(action) }
for (index in 0 until times) {
action(index)
}
}
...
/*
* Counting: 0
* Counting: 1
* Counting: 2
* Counting: 3
* Counting: 4
*/
repeat(5) {
println("Counting: $it")
}
μλ²½ν when κ°μ νκΈ°
ππ» μ»΄νμΌλ¬μκ² else μ μ μꡬνλλ‘ κ°μ
Copy val <T> T.exhaustive: T
get() = this
@Test
fun `test`() {
fun printMod3Exhaustive(n: Int) {
when (n % 3) {
0 -> println("$n % 3 == 0")
1 -> println("$n % 3 == 1")
2 -> println("$n % 3 == 2")
else -> println("Houston, we have a problem...")
}.exhaustive
}
(1..10).forEach { printMod3Exhaustive(it) }
}
μ€ν κ°λ₯ν ν΄λμ€ λ§λ€κΈ°
ν΄λμ€μμ λ¨μΌ ν¨μλ₯Ό κ°λ¨νκ² νΈμΆνκ³ μΆλ€λ©΄,
ν¨μλ₯Ό νΈμΆν ν΄λμ€μμ invoke
μ°μ°μ ν¨μλ₯Ό μ¬μ μνμ.
Copy data class Assignment(
val name: String,
val craft: String
)
data class AstroResult(
val message: String,
val number: Int,
val people: List<Assignment>
)
...
class AstroRequest {
companion object {
private const val ASTRO_URL = "http://api.open-notify.org/astros.json"
}
// invoke μ°μ°μ ν¨μλ₯Ό ν΅ν΄ μ€ν κ°λ₯ν ν΄λμ€λ‘ μ¬μ©
operator fun invoke(): AstroResult =
Gson().fromJson(URL(ASTRO_URL).readText(), AstroResult::class.java)
}
...
@Test
fun `get people in space`() {
// val request = AstroRequest() // AstroRequest ν΄λμ€ μΈμ€ν΄μ€ν
// val result = request() // ν¨μμ²λΌ ν΄λμ€λ₯Ό νΈμΆ(invoke νΈμΆ)
var result = AstroRequest()()
assertAll(
{ assertEquals("success", result.message) },
{ assertTrue { result.number >= 0 } },
{ assertEquals(result.number, result.people.size) }
)
}
invoke
μ°μ°μ ν¨μλ₯Ό μ 곡νκ³ ν΄λμ€ λ νΌλ°μ€μ κ΄νΈλ₯Ό μΆκ°νλ©΄ ν΄λμ€ μΈμ€ν΄μ€λ₯Ό λ°λ‘ μ€νν μ μλ€.
μνλ€λ©΄ νμν μΈμλ₯Ό μΆκ°ν invoke ν¨μ μ€λ³΅λ μΆκ°ν μ μλ€.
κ²½κ³Ό μκ° μΈ‘μ νκΈ°
μ½λ λΈλ‘μ΄ μ€νλλ λ° κ±Έλ¦° μκ°μ μκ³ μΆλ€λ©΄, measureTimeMillis λλ measureNanoTime ν¨μλ₯Ό μ¬μ©νμ.
measureTimeMillis ν¨μμ ꡬν
Copy public inline fun measureTimeMillis(block: () -> Unit): Long {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
val start = System.currentTimeMillis()
block()
return System.currentTimeMillis() - start
}
ππ» μ½λ λΈλ‘μ κ²½κ³Ό μκ° μΈ‘μ νκΈ°
Copy fun doubleIt(x: Int): Int {
Thread.sleep(100L)
println("doubling $x with on thread ${Thread.currentThread().name}")
return x * 2
}
/**
* This machine has 10 processors
* doubling 1 with on thread main
* ...
* doubling 8 with on thread main
* Sequential stream took 845ms
* doubling 6 with on thread main
* doubling 8 with on thread ForkJoinPool.commonPool-worker-2
* ...
* doubling 1 with on thread ForkJoinPool.commonPool-worker-6
* Parallel stream took 106ms
*/
fun main() {
println("This machine has ${Runtime.getRuntime().availableProcessors()} processors")
var time = measureTimeMillis {
IntStream.rangeClosed(1, 8)
.map { doubleIt(it) }
.sum()
}
println("Sequential stream took ${time}ms")
time = measureTimeMillis {
IntStream.rangeClosed(1, 8)
.parallel()
.map { doubleIt(it) }
.sum()
}
println("Parallel stream took ${time}ms")
}
μ€λ λ μμνκΈ°
μ€λ λ νμ₯ ν¨μμ μκ·Έλμ²
Copy public fun thread(
start: Boolean = true,
isDaemon: Boolean = false,
contextClassLoader: ClassLoader? = null,
name: String? = null,
priority: Int = -1,
block: () -> Unit
): Thread
ππ» λ€μμ μ€λ λλ₯Ό μμμ κ°κ²©μΌλ‘ μμνκΈ°
Copy /**
* Thread-0 for 0 after 2ms
* Thread-1 for 1 after 345ms
* Thread-2 for 2 after 370ms
* Thread-3 for 3 after 153ms
* Thread-4 for 4 after 138ms
* Thread-5 for 5 after 879ms
*/
(0..5).forEach { n ->
val sleepTime = Random.nextLong(range = 0..1000L)
thread {
Thread.sleep(sleepTime)
println("${Thread.currentThread().name} for $n after ${sleepTime}ms")
}.join()
}
Thread νμ₯ ν¨μλ μμ μ λ³Έλ¬Έμμ μμ±ν μ€λ λλ₯Ό 리ν΄νλ―λ‘, μ€λ λμ join λ©μλλ₯Ό μ¬μ©ν΄μ λͺ¨λ μ€λ λλ₯Ό μμ°¨μ μΌλ‘ νΈμΆνκ² λ§λ€ μ μλ€.
Copy (0..5).forEach { n ->
thread {
// ...
}.join() // 리ν΄λ μ€λ λμ invoke λ©μλλ₯Ό μ¬μ©ν μ μλ€.
}
TODOλ‘ μμ± κ°μ νκΈ°
ππ» TODO ν¨μμ ꡬν
ν¨μ¨μ±μ μ΄μ λ‘ μμ€λ μΈλΌμΈλμ΄ μκ³ , ν¨μκ° νΈμΆλ λ NotImplementedError λ°μ
Copy public inline fun TODO(reason: String): Nothing =
throw NotImplementedError("An operation is not implemented: $reason")
...
fun main() {
TODO(reason = "none, really")
}
Randomμ 무μμ λμ μ΄ν΄νκΈ°
ππ» nextInt ν¨μ
Copy @Test
internal fun `nextInt with no args gives any Int`() {
val value = Random.nextInt()
assertTrue(value in Int.MIN_VALUE..Int.MAX_VALUE)
}
@Test
internal fun `nextInt with a range gives value between 0 and limit`() {
val value = Random.nextInt(10)
assertTrue(value in 0..10)
}
@Test
internal fun `nextInt with min and max gives value between them`() {
val value = Random.nextInt(5, 10)
assertTrue(value in 5..10)
}
@Test
internal fun `nextInt with range returns value in range`() {
val value = Random.nextInt(7..12)
assertTrue(value in 7..12)
}
ππ» μλ κ°κ³Ό ν¨κ» λμ μμ±κΈ° μ¬μ©νκΈ°
Copy @Test
internal fun `Random function produces a seeded generator`() {
val r1 = Random(12345)
val nums1 = (1..10).map { r1.nextInt() }
val r2 = Random(12345)
val nums2 = (1..10).map { r2.nextInt() }
// println(nums1)
assertEquals(nums1, nums2)
}
μ€νλ§ νλ μμν¬
μ½νλ¦°μΌλ‘ μ€νλ§ μ ν리μΌμ΄μ
μ μμ±ν λ μ¬μ©ν μ μλ λͺ κ°μ§ κΈ°μ λ€
νμ₯μ μν΄ μ€νλ§ κ΄λ¦¬ λΉ ν΄λμ€ μ€ννκΈ°
νμ₯μ μν΄ μλμΌλ‘ νμν μ€νλ§ κ΄λ¦¬ ν΄λμ€λ₯Ό μ΄μ΄μ£Όλ μ½νλ¦° μ€νλ§ νλ¬κ·ΈμΈμ λΉλ νμΌμ μΆκ°νμ
μ½νλ¦°μ κΈ°λ³Έμ μΌλ‘ μ μ μΌλ‘ κ²°ν© νλ€.
ν΄λμ€κ° open ν€μλλ₯Ό μ¬μ©ν΄ νμ₯μ μν μ΄λ¦ΌμΌλ‘ νμλμ§ μμΌλ©΄ λ©μλ μ¬μ μ λλ ν΄λμ€ νμ₯μ΄ λΆκ°λ₯ νλ€.
μ½νλ¦°μ μ΄ λ¬Έμ λ₯Ό all-open
νλ¬κ·ΈμΈμΌλ‘ ν΄κ²°νλ€.
all-open
νλ¬κ·ΈμΈμ ν΄λμ€μ ν΄λμ€μ ν¬ν¨λ ν¨μμ λͺ
μμ μΌλ‘ open ν€μλλ₯Ό μΆκ°νμ§ μκ³ λͺ
μμ μΈ open μ λ
Έν
μ΄μ
μΌλ‘ ν΄λμ€λ₯Ό μ€μ νλ€.
kotlin-spring
νλ¬κ·ΈμΈμ μλ μ λ
Έν
μ΄μ
μΌλ‘ ν΄λμ€λ₯Ό μ΄λλ‘ μ€μ λμ΄ μλ€.
Copy implementation("org.jetbrains.kotlin.plugin.spring:org.jetbrains.kotlin.plugin.spring.gradle.plugin:2.0.21")
μ½νλ¦° data ν΄λμ€λ‘ νΌμμ€ν΄μ€ ꡬννκΈ°
data ν΄λμ€λ‘ JPAλ₯Ό μ¬μ©νκ³ μΆλ€λ©΄ kotlin-jpa νλ¬κ·ΈμΈμ μΆκ°νμ.
JPA κ΄μ μμ data ν΄λμ€λ λ κ°μ§ λ¬Έμ κ° μλ€.
1οΈβ£ JPAλ λͺ¨λ μμ±μ κΈ°λ³Έκ°μ μ 곡νμ§ μλ μ΄μ κΈ°λ³Έ μμ±μκ° νμμ§λ§ data ν΄λμ€λ κΈ°λ³Έ μμ±μκ° μλ€.
no-arg νλ¬κ·ΈμΈμ μΈμκ° μλ μμ±μλ₯Ό μΆκ°ν ν΄λμ€λ₯Ό μ νν μ μκ³
κΈ°λ³Έ μμ±μ μΆκ°λ₯Ό νΈμΆνλ μ λν
μ΄μ
μ μ μν μλ μλ€.
kotlin-jpa νλ¬κ·ΈμΈμ no-arg νλ¬κ·ΈμΈμ κΈ°λ°μΌλ‘ λ§λ€μ΄ μ‘κ³ , μλ μ λ
Έν
μ΄μ
μΌλ‘ μλ νμλ ν΄λμ€μ κΈ°λ³Έ μμ±μλ₯Ό μΆκ°νλ€.
Copy kotlin("plugin.jpa") version "1.2.71"
2οΈβ£ val μμ±κ³Ό ν¨κ» data ν΄λμ€λ₯Ό μμ±νλ©΄ λΆλ³ κ°μ²΄κ° μμ±λλλ°, JPAλ λΆλ³ κ°μ²΄μ λλΆμ΄ μ λμνλλ‘ μ€κ³λμ§ μμλ€.
μν°ν°λ‘ μ¬μ©νκ³ μΆμ μ½νλ¦° ν΄λμ€μ (data ν΄λμ€ λμ ) νλ κ°μ λ³κ²½ν μ μκ² μμ±μ var νμ
μ μ¬μ©νλ λ¨μ ν΄λμ€ μ¬μ©μ μΆμ²
ππ» λ°μ΄ν°λ² μ΄μ€ ν
μ΄λΈλ‘ 맀νλλ μ½νλ¦° ν΄λμ€
Copy @Entity
class Article(
var title: String,
var headline: String,
var content: String,
@ManyToOne var author: User,
var slug: String = title.toSlug(),
var addedAt: LocalDateTime = LocalDateTime.now(),
@Id @GeneratedValue var id: Long? = null)
@Entity
class User(
var login: String,
var firstname: String,
var lastname: String,
var description: String? = null,
@Id @GeneratedValue var id: Long? = null)
μμ‘΄μ± μ£Όμ
νκΈ°
μ½νλ¦° μ€νλ§μ μμ±μ μ£Όμ
μ μ 곡νμ§λ§ νλ μ£Όμ
μλ lateinit var ꡬ쑰λ₯Ό μ¬μ©ν΄μΌ νλ€.
μ νμ μΈ λΉμ λ νμ© νμ
μΌλ‘ μ μΈνλ€.
ν΄λμ€μμ μμ±μκ° νλλΏμ΄λΌλ©΄ μ€νλ§μ΄ μλμΌλ‘ ν΄λμ€μ μ μΌν μμ±μμ λͺ¨λ μΈμλ₯Ό μλμΌλ‘ μ€ν μμ΄μ΄λ§νκΈ° λλ¬Έμ @Autowired μ λ
Έν
μ΄μ
μ μ¬μ©ν νμκ° μλ€.
ππ» μ€νλ§μΌλ‘ μμ‘΄μ± μ€ν μμ΄μ΄λ§νκΈ°
Copy /** λ¨μΌ μμ±μλ₯Ό κ°λ ν΄λμ€ */
@RestController
class GreetingController(val service: GreetingService) { /* ... */ }
/** λͺ
μμ μΌλ‘ μ€ν μμ΄μ΄λ§ */
@RestController
class GreetingController(@Autowired val service: GreetingService) { /* ... */ }
/** μ€ν μμ΄μ΄λ§ μμ±μ νΈμΆ. μ£Όλ‘ λ€μμ μμ‘΄μ±μ κ°λ ν΄λμ€ */
@RestController
class GreetingController @Autowired constructor(val service: GreetingService) {
// ...
}
/** νλ μ£Όμ
(λΉμΆμ²νμ§λ§ μ μ©ν μ μλ€) */
@RestController
class GreetingController {
@Autowired
lateinit var service: GreetingService
// ...
}
ν΄λμ€μ μμ±μ΄ νμκ° μλλΌλ©΄ ν΄λΉ μμ±μ λ νμ© νμ
μΌλ‘ μ μΈν μ μλ€.
ππ» μ ν κ°λ₯ν νλΌλ―Έν°λ₯Ό κ°λ 컨νΈλ‘€λ¬ ν¨μ
Copy @GetMapping("/hello")
fun greetUser(@RequestParam name: String?) =
Greeting(service.sayHello(name ?: "World"))
...
@DataJpaTest
class RepositoriesTests @Autowired constructor (
val entityManager: TestEntityManager,
val userRepository: UserRepository,
val articleRepository: ArticleRepository) {
// ...
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class IntegrationTests(@Autowired val restTemplate: TestRestTemplate) {
// ...
}