Slide 1

Slide 1 text

Kotlinで サーバサイド を始めよう! 2019-05-22 長澤太郎

Slide 2

Slide 2 text

もくじ 1. Kotlinのすごいところ 2. KotlinでSpringアプリを始める 3. Kotlinでユニットテスト

Slide 3

Slide 3 text

事実上のKotlinエバンジェリスト 長澤 太郎

Slide 4

Slide 4 text

医療機関向けサービス AI問診 Ubie Kotlin三兄弟や 現役医師が在籍する 楽しい職場♪

Slide 5

Slide 5 text

1. Kotlinのすごいところ 2. KotlinでWebアプリを始める 3. Kotlinでユニットテスト

Slide 6

Slide 6 text

実践主義 の文法

Slide 7

Slide 7 text

データクラス、プロパティ、コンストラクタ // Java public final class User { private final Long id; private final String name; public Person(Long id, String name) { this.id = id; this.name = name; } public Long getId() { return id; } public String getName() { return name; } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final User user = (User) o; if (!id.equals(user.id)) return false; return name.equals(user.name); } @Override public int hashCode() { int result = id.hashCode(); result = 31 * result + name.hashCode(); return result; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } public User withId(Long id) { return new User(id, name); } public User withName(String name) { return new User(id, name); } public static Builder { private Long id; private String name; public User build() { return new User(id, name); } public Builder id(Long id) { this.id = id; return this; } public Builder name(String name) { this.name = name; return this; } } } data class User( val id: Long, val name: String ) Java Kotlin

Slide 8

Slide 8 text

Null安全 val a: Int = null // コンパイルエラー val b: Int? = null // OK b.toString() // コンパイルエラー s?.length if (s != null) s.length else null 同じ if-elseは式 (評価されて値になる) 厳しいけど、扱いやすい

Slide 9

Slide 9 text

拡張関数 fun String.hello() { println("Hello, ${this}!") } val name = "world" name.hello() // Hello, world!

Slide 10

Slide 10 text

守備範囲 の広さ

Slide 11

Slide 11 text

Kotlinで作れるアプリ ● CLIアプリケーション ● デスクトップアプリケーション ● Androidアプリ ● iOSアプリ ● ブラウザアプリ(AltJS) ● Webアプリケーション

Slide 12

Slide 12 text

マルチプラットフォーム・プロジェクト Common Platform (JVM) Platform (JS) Regular (JVM) Regular (JS) 矢印は依存の方向 // Common module expect fun hello() // JVM module actual fun hello() { System.out.println("Hello!") } // JS module actual fun hello() { console.log("Hello!") }

Slide 13

Slide 13 text

仲間たち

Slide 14

Slide 14 text

Kotlin財団 JetBrains × Google

Slide 15

Slide 15 text

日本Kotlinユーザグループ

Slide 16

Slide 16 text

1. Kotlinのすごいところ 2. KotlinでWebアプリを始める 3. Kotlinでユニットテスト

Slide 17

Slide 17 text

Spring Bootプロジェクトの雛形作成 Webサイト または IntelliJ IDEA で 簡単にプロジェクト雛形を作 成可能

Slide 18

Slide 18 text

アノテーションベースのいつものSpring @SpringBootApplication class DemoApplication fun main(args: Array) { runApplication(*args) } @Service class HelloWorldService { fun helloWorld(): String = "Hello, world!" } @RestController class HelloWorldController(val helloWorldService: HelloWorldService) { @GetMapping("/hello-world") fun helloWorld(): String = helloWorldService.helloWorld() }

Slide 19

Slide 19 text

KotlinDSLによるDIとルーティング設定も fun main(args: Array) { SpringApplicationBuilder() .sources(DemoApplication::class.java) .initializers(beans { bean { HelloWorldService() } bean { HelloWorldController(ref()) } bean { router { GET("/hello-world") { ref().helloWorld() } } } }) .run(*args) } Bean 登録 routing 設定

Slide 20

Slide 20 text

Kotlin x Spring Boot x GraphQL type Query { drugs(yjCode: String!) : [Drug!] } type Drug { yjCode: String! name: String! } @Component class DrugQueryResolver( val drugService: DrugService ): GraphQLQueryResolver { fun drugs(yjCode: String): List { return drugService.getDrugs(yjCode) } } しらじさんによるサンプルコード https://github.com/ubie-inc/kotlin-graphql-sample

Slide 21

Slide 21 text

Kotlin向けWebアプリフレームワーク Ktor ● https://ktor.io ● JetBrainsにより開発 ● いわゆるマイクロ・フレームワーク ● Kotlin DSLによるルーティング設定 ● ノンブロッキング、コルーチン対応 fun main(args: Array) { embeddedServer(Netty, 8080) { routing { get("/") { call.respondText("Hello, world!", ContentType.Text.Html) } } }.start(wait = true) }

Slide 22

Slide 22 text

1. Kotlinのすごいところ 2. KotlinでWebアプリを始める 3. Kotlinでユニットテスト

Slide 23

Slide 23 text

テスト関係のフレームワークやライブラリ ● JUnit5 - Jupiter ● Spring Test - WebTestClient ● AssertJ ● MockK ● DbSetup-kotlin

Slide 24

Slide 24 text

JUnit5 class FooTest { @Nested inner class fooMethod { @Test fun `should throw exception`() { assertThrows() { Foo().foo() } } } }

Slide 25

Slide 25 text

class FooTest { @Nested inner class fooMethod { @Test fun `should throw exception`() { assertThrows() { Foo().foo() } } } } JUnit5 メソッドをグルーピングしてテストの見通しを良く

Slide 26

Slide 26 text

JUnit5 class FooTest { @Nested inner class fooMethod { @Test fun `should throw exception`() { assertThrows() { Foo().foo() } } } }

Slide 27

Slide 27 text

AssertJ val got = sut.findUser(id) assertThat(got).isNotNull assertThat(got?.name).isEqualTo("ほげ")

Slide 28

Slide 28 text

そもそもデータクラスで比較すべし val got = sut.findUser(id) val expected = User(id = 1, name = "ほげ") assertThat(got).isEqualTo(expected)

Slide 29

Slide 29 text

MockK interface UserService { fun createUser(id: Long): User suspend fun findUser(id: Long): User? } val userService = mockk() every { userService.createUser(1) } returns user coEvery { userService.findUser(1) } returns user

Slide 30

Slide 30 text

モック生成は一度、都度リセット class DesignControllerTest { private val repo: DesignRepository = mockk() private val client: DesignClient = mockk() private val controller: DesignController(repo, client) @BeforeEach fun init() { clearMocks(repo, client) } } 参考 https://www.youtube.com/watch?v=RX_g65J14H0

Slide 31

Slide 31 text

WebTestClient - expectBody拡張関数 webTestClient.get() .exchange() .expectBody() .isEqualTo(expectedBody) import org.springframework.test.web.reactive.server.expectBody

Slide 32

Slide 32 text

まとめ ● Kotlin 書きやすく読みやすい! ● カバーしている環境が多岐に渡る ● 強力な後ろ盾があり、コミュニティもよさそう ● Kotlinでも普通にSpringが使える ● GraphQLも簡単にできそう ● データクラスの内容をテストするときはデータクラスと比較する とレポートが読みやすい ● モックライブラリはMockKがよさそう   サーバーサイドKotlin よい