Kotlinで使う際のTipsやハマりポイントも紹介 • 2部: How do we test Server-Side Kotlin(鈴木) ◦ 実際のプロジェクトでどのようにテストを書いていたかを紹介 • 3部: Our test tools for Kotlin(鈴木) ◦ テストのために発表者が作ったツールを紹介 11
* @Ruleの@Targetにより導かれる。詳細はKotlin公式Doc参照 ◦ http://kotlinlang.org/docs/reference/annotations.html#annotation-use-site-targets • @Ruleをつけるフィールドはpublicでなければいけないが、上記の通りフィール ドがprivateなので、must be publicというエラーになる ◦ https://junit.org/junit4/javadoc/4.12/org/junit/Rule.html class SampleTest { @Rule val myRule = MyRule() // org.junit.internal.runners.rules.ValidationError: The @Rule 'myRule' must be public. 19
Bytecodeの結果抜粋 public final class SampleTest { private final LMyRule; myRule @Lorg/junit/Rule;() public final getMyRule()LMyRule; 20 privateなフィールドと publicなgetterが生成されている @Ruleはprivateなフィールドの方 に付与されている
SampleTest { @JvmField @Rule val myRule = MyRule() ↓ // Show Kotlin Bytecodeの結果抜粋 public final class SampleTest { public final LMyRule; myRule @Lorg/junit/Rule;() 21
Kotlin class SampleTest { @get:Rule val myRule = MyRule() ↓ // Show Kotlin Bytecodeの結果抜粋 public final class SampleTest { private final LMyRule; myRule public final getMyRule()LMyRule; @Lorg/junit/Rule;() 22
publicというエラーになる @RunWith(Theories::class) class TheoriesTest { companion object { @DataPoints val values = listOf( Data(1, 2, 3), Data(1, -2, -1) ) // DataPoint field values must be public 25
classのためモックできません」というエラーになる • 主な対処法4つ class ApiClient { fun foo(): String { return "api response" } } val mock = mock(ApiClient::class.java) // Mockito cannot mock/spy because : - final class 48
前にnullチェックするため、エラーになる open class ApiClient { open fun foo(config: Config /** NotNullな引数 */){ … } } val mockClient = Mockito.mock(ApiClient::class.java) Mockito.`when`(mockClient.foo(Mockito.any())).thenReturn("dummy") // java.lang.IllegalStateException: Mockito.any() must not be null 55
val iDBConn = DatabaseConnection(conn) val iDataSet = FlatXmlDataSetBuilder().build(File("src/test/resources/dbunit-datas/users.xml")) DatabaseOperation.CLEAN_INSERT.execute(iDBConn, iDataSet) } @Test fun `test 1`() { val stmt = conn.createStatement() val rs = stmt.executeQuery("select * from users order by id asc") assertTrue(rs.next()) assertEquals("maeharin", rs.getString("name")) } 67 DBUnit - コード例
to "maeharin", "job" to "ENGINEER", "status" to "ACTIVE", "age" to 30, "is_admin" to false, "created_timestamp" to LocalDateTime.now() ) } }.launch() 70 DbSetup - コード例
= "{\"id\":1,\"friends\":[3,4]}" assertJsonEquals(a, b) // java.lang.AssertionError: JSON documents are different: //Different value found in node "friends[0]", expected: <1> but was: <3>. //Different value found in node "friends[1]", expected: <2> but was: <4>. 106
) { fun calculateDistances(other: GecCode): Long {} } @Test fun 正しく距離が計算されること(){ val geoCode = GeoCode(xxx, yyy) val otherGeoCode = GeoCode(zzzz, aaaa) assertEquals(geoCode.calculateDistance(otherGeoCode), xxxx) } 110
VARCHAR(256) NOT NULL, job VARCHAR(256) NOT NULL DEFAULT 'engineer', status VARCHAR(256) NOT NULL DEFAULT 'ACTIVE', age INTEGER NOT NULL, score NUMERIC NOT NULL, is_admin BOOLEAN NOT NULL, birth_day DATE NOT NULL, nick_name VARCHAR(256), created_timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL, updated_timestamp TIMESTAMP WITHOUT TIME ZONE ); • 左記のようなスキーマに対して、 factlinのgradle タスクを実行する ./gradlew factliin 117
id: Int = 0, // primary key val name: String = "", // user name val job: String = "", // job name val status: String = "", // activate status val age: Int = 0, // user age val score: BigDecimal = 0.toBigDecimal(), // game score val is_admin: Boolean = false, // user is admin user or not val birth_day: LocalDate = LocalDate.now(), // user birth day val nick_name: String? = null, // nick name val created_timestamp: LocalDateTime = LocalDateTime.now(), val updated_timestamp: LocalDateTime? = null ) 118
to f.id, "name" to f.name, "job" to f.job, "status" to f.status, "age" to f.age, "score" to f.score, "is_admin" to f.is_admin, "birth_day" to f.birth_day, "nick_name" to f.nick_name, "created_timestamp" to f.created_timestamp, "updated_timestamp" to f.updated_timestamp ) } } 119
val age: Int, val address: String, val firstName: String, val lastName: String, val firstNameKana: String, val lastNameKana: String, val favoriteSongName: String, val favoriteFruitsName: String, val favoriteSport: String, val favoriteProgramingLanguage: String, val favoriteBook: String, val isMarried: Boolean, val hasDependent: Boolean, val CommutingTime: Int, val nearestStationName: String, val isAdmin: Boolean, val educationBackground: String, val githubAccount: String, val twitterAccount: String, val facebookAccount: String, val linkedinAccount: String, val lineAccount: String, val m3Account: String, val birthday: LocalDate, val motherTongue: String, val occupation: String, val bestSubject: String, val deathblow: String, val youtubeAccount: String, val lovesKotlin: Boolean ) 124