今年の7月に正式リリースされたGoogle製のアサーションライブラリであるTruthですが、調べてみると今後のAndroidではTruthを正式に使っていく流れになりそうでした(あくまで個人の予想ですが)。 なので、既存のアサーションライブラリと比較して、置き換えをしても問題ないかを検証しました。
株式会社 ヤプリXXXX株式会社 ヤプリTruthとAssertJを⽐較してみた / Dais-33
View Slide
株式会社 ヤプリ株式会社 ヤプリ• NameØ 佐々⽊ ⼤輔• TwitterØ @DaisSasa• CompanyØ 株式会社ヤプリ• JobØ Androidエンジニア⾃⼰紹介Copyright © 2019 Yappli, Inc. All rights reserved.1
株式会社 ヤプリ株式会社 ヤプリ• サービス名Ø yappli• サービス内容Ø ⾮エンジニアがアプリ開発・運⽤できるようになるプラットフォームØ ⼀部機能を除きほぼネイティブで提供• エンジニアの役割Ø yappliで製作するアプリで使われる機能の原型の実装、改修を⾏いますAndroidに限らずアプリエンジニア募集中です興味がある⽅は、良ければ懇親会にてお声掛け下さいhttps://yapp.li/サービス紹介Copyright © 2019 Yappli, Inc. All rights reserved.2
株式会社 ヤプリ株式会社 ヤプリTruthを試してみた背景Copyright © 2019 Yappli, Inc. All rights reserved.3• TruthはGoogleが提供しているアサーションライブラリØ 2019年7⽉8⽇にバージョン1.0が正式にリリースされたばかり• Google社内ではこのTruthを使って開発を⾏っているØ Truthの公式ページにて記載されている• AndroidX Test LibraryにてTruth向けの拡張が⽤意されてある今後TruthがAndroidの正式なアサーションライブラリになるかも︖また、Android向けの開発も継続的に⾏われると予想(あくまで個⼈の予想です)
株式会社 ヤプリ株式会社 ヤプリTruthを試してみた背景Copyright © 2019 Yappli, Inc. All rights reserved.4とはいえ、まだまだ正式リリースされたばかり、既存のライブラリから置き換えても⼤丈夫か︖↓という訳で、アサーションライブラリでよく使われているAssertJと使える機能の⽐較を⾏いました
株式会社 ヤプリ株式会社 ヤプリ⽂字列のアサーションCopyright © 2019 Yappli, Inc. All rights reserved.5AssertJAssertions.assertThat("TOKYO").`as`("TEXT CHECK TOKYO").isEqualTo("TOKYO").isEqualToIgnoringCase("tokyo").isNotEqualTo("KYOTO").isNotBlank().startsWith("TO").endsWith("YO").contains("OKY").matches("[A-Z]{5}").isInstanceOf(String::class.java)
株式会社 ヤプリ株式会社 ヤプリ⽂字列のアサーションCopyright © 2019 Yappli, Inc. All rights reserved.6TruthTruth.assertWithMessage("TEXT CHECK TOKYO").that("TOKYO").apply {isEqualTo("TOKYO")// isEqualToIgnoringCase("tokyo")isNotEqualTo("KYOTO")isNotEmpty()startsWith("TO")endsWith("YO")contains("OKY")matches("[A-Z]{5}")isInstanceOf(String::class.java)}
株式会社 ヤプリ株式会社 ヤプリ数値のアサーションCopyright © 2019 Yappli, Inc. All rights reserved.7AssertJAssertions.assertThat(3.14159).isNotZero().isNotNegative().isGreaterThan(3.0).isLessThanOrEqualTo(4.0).isBetween(3.0, 3.2).isCloseTo(Math.PI, within(0.001))
株式会社 ヤプリ株式会社 ヤプリ数値のアサーションCopyright © 2019 Yappli, Inc. All rights reserved.8TruthTruth.assertThat(3.14159).apply {isNonZero()isAtLeast(0.0)isGreaterThan(3.0)isAtMost(4.0)// isBetween(3.0, 3.2)// isCloseTo(Math.PI, within(0.001))}
株式会社 ヤプリ株式会社 ヤプリコレクションのアサーションCopyright © 2019 Yappli, Inc. All rights reserved.9AssertJval target = listOf("Giants", "Dodgers", "Athletics")Assertions.assertThat(target).hasSize(3).contains("Dodgers").containsOnly("Athletics", "Dodgers", "Giants").containsExactly("Giants", "Dodgers", "Athletics").doesNotContain("Padres")
株式会社 ヤプリ株式会社 ヤプリコレクションのアサーションCopyright © 2019 Yappli, Inc. All rights reserved.10Truthval target = listOf("Giants", "Dodgers", "Athletics")Truth.assertThat(target).apply {hasSize(3)contains("Dodgers")containsAtLeast("Athletics", "Dodgers", "Giants")containsExactly("Giants", "Dodgers","Athletics").inOrder()doesNotContain("Padres")}
株式会社 ヤプリ株式会社 ヤプリフィルタリングCopyright © 2019 Yappli, Inc. All rights reserved.11AssertJval target = listOf(BallTeam("Giants", "San Francisco", "AT&T Park"),BallTeam("Dodgers", "Los Angels", "Dodger Stadium"),BallTeam("Angels", "Los Angels", "Angel Stadium"),BallTeam("Athletics", "Oakland", "Oakland Coliseum"),BallTeam("Padres", "San Diego", "Petco Park"))Assertions.assertThat(target).filteredOn { team -> team.city.startsWith("San") }.filteredOn { team -> team.city.endsWith("Francisco") }.extracting("name", String::class.java).containsExactly("Giants")Assertions.assertThat(target).filteredOn { team -> team.city == "Los Angels" }.extracting("name", "stadium").containsExactly(tuple("Dodgers", "Dodger Stadium"),tuple("Angels", "Angel Stadium"))
株式会社 ヤプリ株式会社 ヤプリフィルタリングCopyright © 2019 Yappli, Inc. All rights reserved.12Truthval target = listOf(BallTeam("Giants", "San Francisco", "AT&T Park"),BallTeam("Dodgers", "Los Angels", "Dodger Stadium"),BallTeam("Angels", "Los Angels", "Angel Stadium"),BallTeam("Athletics", "Oakland", "Oakland Coliseum"),BallTeam("Padres", "San Diego", "Petco Park"))// Truthにfilter機能がない?ので自分でfilterする// 一部の要素のみをみて比較する場合、Correspondenceを定義して行う// 比較する要素ごとにCorrespondenceの定義が必要になるval filterTarget = target.filter { team -> team.city.startsWith("San") }.filter { team -> team.city.endsWith("Francisco") }Truth.assertThat(filterTarget).comparingElementsUsing(HAS_NAME).containsExactly("Giants")val filterTarget2 = target.filter { team -> team.city == "Los Angels" }Truth.assertThat(filterTarget2).comparingElementsUsing(BALLTEAM_NAME_PARSES_TO_TUPLE).containsExactly(tuple("Dodgers", "Dodger Stadium"),tuple("Angels", "Angel Stadium"))
株式会社 ヤプリ株式会社 ヤプリフィルタリング_truthの補⾜Copyright © 2019 Yappli, Inc. All rights reserved.13Truthprivate val HAS_NAME = Correspondence.transforming({ it?.name }, "Has anName of")private val BALLTEAM_NAME_PARSES_TO_TUPLE = Correspondence.from(::ballteamParsesToTuple,"parses to")// Ballteamのnameとstadiumの⽐較private fun ballteamParsesToTuple(actual: BallTeam?, expected: Tuple?): Boolean {if (actual == null) {return expected == null}return expected?.toList()?.let { list ->if (list.isEmpty() || list.size != 2) return falseactual.name == list[0] && actual.stadium == list[1]} ?: false}
株式会社 ヤプリ株式会社 ヤプリ例外の検証Copyright © 2019 Yappli, Inc. All rights reserved.14AssertJAssertions.assertThatExceptionOfType(RuntimeException::class.java).isThrownBy { functionMayThrow() }.withMessage("Aborted!").withNoCause()
株式会社 ヤプリ株式会社 ヤプリ例外の検証Copyright © 2019 Yappli, Inc. All rights reserved.15Truthtry {functionMayThrow()} catch (e: Throwable) {Truth.assertThat(e).apply {isInstanceOf(RuntimeException::class.java)hasMessageThat().contains("Aborted!")// withNoCause()}}
株式会社 ヤプリ株式会社 ヤプリ実際に動くテストコードCopyright © 2019 Yappli, Inc. All rights reserved.16スライドに記載したテストコードは発表⽤にガリガリ削ってます実際に動くものはGithubにて公開してますhttps://github.com/dais-33/android-truth
株式会社 ヤプリ株式会社 ヤプリ⽐較してみた感想・結論Copyright © 2019 Yappli, Inc. All rights reserved.17• 流⽯にAssertJができることを全て網羅している訳ではない• ただし、必要最低限は網羅していそうØ それはテストする必要があるのか︖という機能を削った印象↓すでにAssertJ等を使ってバリバリテストを書いてるアプリではTruthに切り替える理由は薄いこれからアサーションライブラリを導⼊しようと考えている場合はTruthも選択として⼗分有り
株式会社 ヤプリ株式会社 ヤプリ発表は以上になります。ご静聴ありがとうございました。Copyright © 2019 Yappli, Inc. All rights reserved.18