Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Spek2+MockK+JaCoCoでイケてる Unit Test環境を手に入れろ!
Search
subroh_0508
February 08, 2019
Technology
9
5.3k
Spek2+MockK+JaCoCoでイケてる Unit Test環境を手に入れろ!
DroidKaigi 2019 - Day2 Room1 11:20〜
「Spek2+MockK+JaCoCoでイケてる Unit Test環境を手に入れろ!」のセッション資料です
subroh_0508
February 08, 2019
Tweet
Share
More Decks by subroh_0508
See All by subroh_0508
Compose MultiplatformでもHot Reloadが動くらしい
subroh0508
1
100
Compose for Webでポートフォリオサイトを作る
subroh0508
1
150
あらゆるアプリをCompose Multiplatformで書きたい! -ネイティブアプリの「あの機能」を私たちはどう作るか-
subroh0508
1
2.6k
登壇の心理的ハードルをコントロールする技術
subroh0508
2
550
テストコードを書きながらCompose Multiplatformを乗りこなす
subroh0508
0
890
自己効力感を二次元アイドル作品から得ながら社会人としての成長を超加速させる
subroh0508
2
710
担当アイドルを応援する傘を作ろう! (として失敗した話)
subroh0508
0
610
buildSrc/Composite Buildで必要なバージョン情報も Version Catalogから参照したい!
subroh0508
0
1.6k
フロントエンドもJetpack Composeで書きたい! -Compose for WebはモダンWebアプリケーションの夢を見るか?-
subroh0508
0
930
Other Decks in Technology
See All in Technology
Iceberg Meetup Japan #1 : Iceberg and Databricks
databricksjapan
0
370
Fraxinus00tw assembly manual
fukumay
0
110
OPENLOGI Company Profile for engineer
hr01
1
20k
AI Agent時代なのでAWSのLLMs.txtが欲しい!
watany
2
230
自分だけの仮想クラスタを高速かつ効率的に作る kubefork
donkomura
0
100
ディスプレイ広告(Yahoo!広告・LINE広告)におけるバックエンド開発
lycorptech_jp
PRO
0
380
Windows の新しい管理者保護モード
murachiakira
0
200
データエンジニアリング領域におけるDuckDBのユースケース
chanyou0311
9
2.2k
ウォンテッドリーのデータパイプラインを支える ETL のための analytics, rds-exporter / analytics, rds-exporter for ETL to support Wantedly's data pipeline
unblee
0
130
Exadata Database Service on Cloud@Customer セキュリティ、ネットワーク、および管理について
oracle4engineer
PRO
2
1.5k
PHPで印刷所に入稿できる名札データを作る / Generating Print-Ready Name Tag Data with PHP
tomzoh
0
190
偏光画像処理ライブラリを作った話
elerac
1
170
Featured
See All Featured
StorybookのUI Testing Handbookを読んだ
zakiyama
28
5.5k
Embracing the Ebb and Flow
colly
84
4.6k
Scaling GitHub
holman
459
140k
Building an army of robots
kneath
303
45k
A designer walks into a library…
pauljervisheath
205
24k
Building Adaptive Systems
keathley
40
2.4k
[RailsConf 2023] Rails as a piece of cake
palkan
53
5.3k
Optimizing for Happiness
mojombo
376
70k
Product Roadmaps are Hard
iamctodd
PRO
50
11k
Writing Fast Ruby
sferik
628
61k
It's Worth the Effort
3n
184
28k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
33
2.1k
Transcript
4QFL .PDL, +B$P$PͰΠέͯΔ 6OJU5FTUڥΛखʹೖΕΖʂ %SPJE,BJHJ%BZ3PPNʙ ʹ͜͠Γ͞ͿΖʙ!TVCSPI@ !1
ࣗݾհ w ʹ͜͠Γ͞ͿΖʙ ຊ໊ࡔ্৴ w ౦ژɾҏ౾େౡग़ w גࣜձࣾ#FBS5BJM w
"OESPJEΤϯδχΞ ,PUMJO+BWB w 8FCΤϯδχΞ 3BJMT3FBDU !2
ࣄ w %S8BMMFU ,PUMJOҠߦ࡞ۀதʜ !3
ࣄ w %Sܦඅਫ਼ࢉ ϑϧ,PUMJOʂ .PDL,4QFL ಋೖࡁΈʂ !4
࣍ ͜Ε·Ͱͷ6OJU5FTU ,PUMJOϑϨʔϜϫʔΫϥΠϒϥϦͷհ w .PDL,ϞοΫϥΠϒϥϦ w 4QFLςετϑϨʔϜϫʔΫ
,PUMJOϑϨϯυϦʔͳ6OJU5FTUڥͷߏங w .PDL,ɾ4QFLͷಋೖ w +B$P$PΛͬͨίʔυΧόϨοδࢉग़ !5
ର w ॳΊͯ"OESPJE։ൃͰ6OJU5FTUΛॻ͜͏ͱ͍ͯ͠Δਓ w +BWBϥΠϒϥϦͰͷςετʹർΕͨਓ !6
͜Ε·Ͱͷ6OJU5FTU !7
,PUMJOਖ਼ࣜαϙʔτલ w .PDLJUPϞοΫϥΠϒϥϦ w WSFMFBTFʙࠒ w ཉ͍͠ػೳେମἧ͍ͬͯΔ w ใ͕๛ w
,PUMJO༻ϥούʔϥΠϒϥϦΞϦ w .PDLJUP,PUMJO ݄ʙ !8
w +6OJUςετϑϨʔϜϫʔΫ w "OESPJEϓϩδΣΫτͰͷσϑΥϧτͷϑϨʔϜϫʔΫ w BOOPUBUJPOͰςετϝιουΛࢦఆ ,PUMJOਖ਼ࣜαϙʔτલ !9
,PUMJOਖ਼ࣜαϙʔτલ w ϞοΫϥΠϒϥϦˠ.PDLJUP w ϢχοτςετϑϨʔϜϫʔΫˠ+6OJU w ͲͪΒ+BWB w ,PUMJOରԠ͕ෆे w
,PUMJOͷݴޠ༷Λ׆͔͖͠Εͳ͍ !10
,PUMJOਖ਼ࣜαϙʔτ !(PPHMF*0 !11
ޙʜ w ,PUMJOϥΠϒϥϦϑϨʔϜϫʔΫ w ,PUMJOͷݴޠ༷Λϑϧʹ׆͔ͤΔʂ w ʮ+BWB͔Βݟͨ࣌͜ͷίʔυͲ͏ͳΔΜ͚ͩͬʯˠগͳ͘ͳΔ w ςετॻ࣌͘ʹߟ͑Δ͖͜ͱ͕ݮΔˠܧଓੑͷ͋Δςετ w
҆৺ͯ͑͠Δʂ w ใ͕૿͖͑ͯͨʂ ,PUMJOϥΠϒϥϦϑϨʔϜϫʔΫ ಋೖͷػʂ !12
,PUMJO ϑϨʔϜϫʔΫϥΠϒϥϦͷհ !13
.PDL, w ϞοΫϥΠϒϥϦ w ,PUMJOಠࣗͷݴޠ༷Λཏ w 6OJU5FTU*OTUSVNFOUFE5FTU྆ରԠ w %SPJE,BJHJެࣜΞϓϦͰར༻ (JUIVCNPDLLNPDLL
!14
Ͱ͖Δ͜ͱ w ελϒ w ϝιουͷฦΓͷࠩ͠ସ͑ w ϞοΫ w ϝιουͷҾɺݺΕͨճɺฦΓͷݕূ w
ϝιουͷॲཧಈ࡞͠ͳ͍ w εύΠ w ϝιουͷҾɺݺΕͨճɺฦΓͷݕূ w ϝιουͷॲཧ͕࣮ࡍʹಈ࡞͢Δ ৄ͘͠ "OESPJEςετશॻͰʜ !15
NPDLL5ϝιου 1 val car = mockk<Car>() 2 3 every {
car.drive(Direction.NORTH) } returns Outcome.OK 4 5 car.drive(Direction.NORTH) // returns OK 6 7 verify(exactly = 1) { car.drive(Direction.NORTH) } 8 9 confirmVerified(car) !16
NPDLL5ϝιου 1 val car = mockk<Car>() 2 3 every {
car.drive(Direction.NORTH) } returns Outcome.OK 4 5 car.drive(Direction.NORTH) // returns OK 6 7 verify(exactly = 1) { car.drive(Direction.NORTH) } 8 9 confirmVerified(car) NPDLL5 ϝιου $BSܕͷϞοΫΠϯελϯεΛ࡞ !17
NPDLL5ϝιου 1 val car = mockk<Car>() 2 3 every {
car.drive(Direction.NORTH) } returns Outcome.OK 4 5 car.drive(Direction.NORTH) // returns OK 6 7 verify(exactly = 1) { car.drive(Direction.NORTH) } 8 9 confirmVerified(car) FWFSZ\ʜ^ϝιου ϞοΫʹϝιουͱҾɺฦΓΛڭ͑ࠐΉ ESJWFϝιουͷ࣮ࡍͷॲཧಈ࡞͠ͳ͍ !18
NPDLL5ϝιου 1 val car = mockk<Car>() 2 3 every {
car.drive(Direction.NORTH) } returns Outcome.OK 4 5 car.drive(Direction.NORTH) // returns OK 6 7 verify(exactly = 1) { car.drive(Direction.NORTH) } 8 9 confirmVerified(car) DBSʹ wʮ%JSFDUJPO/035)ʯΛҾʹड͚ wʮ0VUDPNF0,ʯΛฦ͢ wʮESJWFʯϝιου͕͋Δɹ͜ͱΛڭ͑ࠐΉ !19
NPDLL5ϝιου 1 val car = mockk<Car>() 2 3 every {
car.drive(Direction.NORTH) } returns Outcome.OK 4 5 car.drive(Direction.NORTH) // returns OK 6 7 verify(exactly = 1) { car.drive(Direction.NORTH) } 8 9 confirmVerified(car) WFSJGZ\ʜ^ϝιου WFSJGZͷϝιου͕ݺΕͨ͜ͱΛνΣοΫ ҾFYBDUMZʹݺΕΔճΛࢦఆͰ͖Δ !20
TQZL5ϝιου 1 val car = spyk(Car()) 2 3 car.drive(Direction.NORTH) 4
5 verify { car.drive(Direction.NORTH) } 6 7 confirmVerified(car) !21
TQZL5ϝιου 1 val car = spyk(Car()) 2 3 car.drive(Direction.NORTH) 4
5 verify { car.drive(Direction.NORTH) } 6 7 confirmVerified(car) TQZL5 ϝιου $BSܕͷεύΠΠϯελϯεΛ࡞ !22
TQZL5ϝιου 1 val car = spyk(Car()) 2 3 car.drive(Direction.NORTH) 4
5 verify { car.drive(Direction.NORTH) } 6 7 confirmVerified(car) WFSJGZ\ʜ^ϝιου WFSJGZͷϝιου͕ݺΕͨ͜ͱΛνΣοΫ ESJWFϝιουͷॲཧ͕࣮ࡍʹಈ࡞͢Δ !23
1 val car = mockk<Car>() 2 3 // any(): ͋ΒΏΔ1ͭͷҾʹϚον
4 every { car.drive(any()) } return Outcome.OK 5 6 // isNull(inverse = true): nullͰͳ͍1ͭͷҾʹϚον 7 every { car.drive(isNull(inverse = true)) } return Outcome.OK 8 9 // eq(value): Ҿ͕valueͱҰக͢Δ࣌Ϛον(deep equals) 10 every { car.drive(eq(Direction.NORTH)) } return Outcome.OK 11 12 // ofType(kClass): Ҿͷܕ͕kClassͱҰக͢Δ࣌ʹϚον 13 every { car.drive(ofType(String::class)) } return Outcome.OK 14 ϚονϟʔҾฦΓͷݕূʹ͏ϝιου BOENPSFʜ !24
ϞοΫεύΠϚονϟʔ.PDLJUPʹ͋ΔΑʁʁʁ !25
.PDLJUPͷ w 'JOBMͳΫϥεϝιου͕ϞοΫͰ͖ͳ͍ʂ w ,PUMJOͷΫϥεϝιουσϑΥϧτͰ'JOBM w ճආࡦLPUMJOBMMPQFOϓϥάΠϯ CZ+FU#SBJOT
w BOOPUBUJPO͚ͭΔͱPQFOʹͯ͘͠ΕΔɹ ڧҾͩͳʜ w ςετͷͨΊʹຊ൪ίʔυͷ༨ܭͳهड़͕૿͑Δ !26
.PDLJUPͷ w 4UBUJDͳϝιου͕ϞοΫͰ͖ͳ͍ʂ w ֦ுؔɹγϯάϧτϯΦϒδΣΫτ w ճආࡦ1PXFS.PDL w 4UBUJDϝιουɺQSJWBUFϝιουͷϞοΫ͕Ͱ͖Δ
w Θ͟Θ͟ผϥΠϒϥϦΛಋೖ͢Δ͜ͱ͕ඍົ !27
.PDL,ͷڧΈ w ,PUMJOͷΫϥεϝιουΛ΄΅ԿͰϞοΫͰ͖ͯ͠·͏ʂ w 'JOBMͳΫϥεϝιου w γϯάϧτϯΦϒδΣΫτ w ֦ுؔ w
QSJWBUFϝιου w TVTQFOEϝιουFUD !28
γϯάϧτϯΦϒδΣΫτ 1 object MockObj { 2 fun add(a: Int, b:
Int) = a + b 3 } 4 5 mockkObject(MockObj) 6 7 assertEquals(3, MockObj.add(1, 2)) 8 9 every { MockObj.add(1, 2) } returns 55 10 11 assertEquals(55, MockObj.add(1, 2)) !29
γϯάϧτϯΦϒδΣΫτ 1 object MockObj { 2 fun add(a: Int, b:
Int) = a + b 3 } 4 5 mockkObject(MockObj) 6 7 assertEquals(3, MockObj.add(1, 2)) 8 9 every { MockObj.add(1, 2) } returns 55 10 11 assertEquals(55, MockObj.add(1, 2)) !30
γϯάϧτϯΦϒδΣΫτ 1 object MockObj { 2 fun add(a: Int, b:
Int) = a + b 3 } 4 5 mockkObject(MockObj) 6 7 assertEquals(3, MockObj.add(1, 2)) 8 9 every { MockObj.add(1, 2) } returns 55 10 11 assertEquals(55, MockObj.add(1, 2)) !31
1 // ϞοΫରͷ֦ுؔ(File.ktʹ࣮͞Ε͍ͯΔͱԾఆ) 2 fun File.extension() = absolutePath.split(“.”).last() 3 4
// ύοέʔδ໊ʮpkg.exampleʯ 5 mockkStatic(“pkg.example.FileKt”) 6 7 every { File(“test.txt”).extension() } returns “txt" 8 9 assertEquals(“txt”, File(“test.txt").extension()) 10 11 verify { File(“test.txt”).extension() } ֦ுؔ !32
1 // ϞοΫରͷ֦ுؔ(File.ktʹ࣮͞Ε͍ͯΔͱԾఆ) 2 fun File.extension() = absolutePath.split(“.”).last() 3 4
// ύοέʔδ໊ʮpkg.exampleʯ 5 mockkStatic(“pkg.example.FileKt”) 6 7 every { File(“test.txt”).extension() } returns “txt" 8 9 assertEquals(“txt”, File(“test.txt").extension()) 10 11 verify { File(“test.txt”).extension() } ֦ுؔ NPDLL4UBUJDʹόΠτίʔυมޙͷ ֦ுؔͷύεΛ͢ ˞5PPMT,PUMJO4IPX,PUMJO#ZUFDPEF !33
1 // ϞοΫରͷ֦ுؔ(File.ktʹ࣮͞Ε͍ͯΔͱԾఆ) 2 fun File.extension() = absolutePath.split(“.”).last() 3 4
// ύοέʔδ໊ʮpkg.exampleʯ 5 mockkStatic(“pkg.example.FileKt”) 6 7 every { File(“test.txt”).extension() } returns “txt" 8 9 assertEquals(“txt”, File(“test.txt").extension()) 10 11 verify { File(“test.txt”).extension() } ֦ுؔ !34
ͦͷଞʹ༷ʑͳϝιου͕༻ҙ ˠ.PDL,ͷ3&"%.& IUUQTNPDLLJP Λࢀর !35
4QFL w ςετϑϨʔϜϫʔΫ w 3VCZͷ34QFDͱࣅͨจ๏ w +6OJU্Ͱಈ࡞ w 6OJU5FTUͷΈରԠ w
3PCPMFDUSJDඇରԠ *TTVFʹ্͕͍ͬͯΔ w Wਖ਼ࣜϦϦʔε લ (JUIVCTQFLGSBNFXPSLTQFL ˞3PPNʙͷ ɹηογϣϯͰհ W !36
4QFL֎؍ w ྫ +40/Λ%PNBJO0CKFDUʹม͢Δ֦ுؔͷ6OJU5FTU 1 // JSONΛද͢Ϋϥε 2 data class
PersonJson(val name: String, val sex: String) 3 4 // Domain Object 5 interface Person 6 7 data class Male(val name: String): Person 8 9 data class Female(val name: String): Person 10 11 // ςετରͷ֦ுؔ 12 fun PersonJson.toPerson(): Person = … !37
1 @RunWith(JUnitPlatform::class) 2 object EntryFormMapperSpec : Spek({ 3 describe("PersonJson#toPerson") {
4 lateinit var json: PersonJson 5 6 beforeEachTest { 7 mockkStatic(“example.PersonJsonKt") 8 } 9 10 context("when sex is male”) { 11 beforeEachTest { 12 json = mockk<PersonJson>(“json1”).also { 13 every { it.toPerson() } returns mockk<Male>() 14 } 15 } 16 17 it("should return Male instance") { 18 assertTrue(json.toPerson() is Male) 19 } 20 } 21 … 22 afterEachTest { 23 unmockkStatic(“example.PersonJsonKt") 24 } 25 } 26 } !38
1 @RunWith(JUnitPlatform::class) 2 object EntryFormMapperSpec : Spek({ 3 describe("PersonJson#toPerson") {
4 lateinit var json: PersonJson 5 6 beforeEachTest { 7 mockkStatic(“example.PersonJsonKt") 8 } 9 10 context("when sex is male”) { 11 beforeEachTest { 12 json = mockk<PersonJson>(“json1”).also { 13 every { it.toPerson() } returns mockk<Male>() 14 } 15 } 16 17 it("should return Male instance") { 18 assertTrue(json.toPerson() is Male) 19 } 20 } 21 … 22 afterEachTest { 23 unmockkStatic(“example.PersonJsonKt") 24 } 25 } 26 } 4QFLΫϥεΛܧঝͨ͠γϯάϧτϯΦϒδΣΫτʹ ςετέʔεΛهड़ !39
1 @RunWith(JUnitPlatform::class) 2 object EntryFormMapperSpec : Spek({ 3 describe("PersonJson#toPerson") {
4 lateinit var json: PersonJson 5 6 beforeEachTest { 7 mockkStatic(“example.PersonJsonKt") 8 } 9 10 context("when sex is male”) { 11 beforeEachTest { 12 json = mockk<PersonJson>(“json1”).also { 13 every { it.toPerson() } returns mockk<Male>() 14 } 15 } 16 17 it("should return Male instance") { 18 assertTrue(json.toPerson() is Male) 19 } 20 } 21 … 22 afterEachTest { 23 unmockkStatic(“example.PersonJsonKt") 24 } 25 } 26 } w1FSTPO+TPOUP1FSTPOϝιου͕ wTFYNBMFͷ࣌ wਖ਼͍͠1FSTPOΠϯελϯεΛฦ͔͢Ͳ͏͔ !40
1 @RunWith(JUnitPlatform::class) 2 object EntryFormMapperSpec : Spek({ 3 describe("PersonJson#toPerson") {
4 lateinit var json: PersonJson 5 6 beforeEachTest { 7 mockkStatic(“example.PersonJsonKt") 8 } 9 10 context("when sex is male”) { 11 beforeEachTest { 12 json = mockk<PersonJson>(“json1”).also { 13 every { it.toPerson() } returns mockk<Male>() 14 } 15 } 16 17 it("should return Male instance") { 18 assertTrue(json.toPerson() is Male) 19 } 20 } 21 … 22 afterEachTest { 23 unmockkStatic(“example.PersonJsonKt") 24 } 25 } 26 } EFTDSJCFDPOUFYUΛೖΕࢠʹΈ ςετରɾ݅Λهड़͢Δ EFTDSJCFςετର DPOUFYUςετ݅Λॻ͘͜ͱ͕ଟ͍ !41
1 @RunWith(JUnitPlatform::class) 2 object EntryFormMapperSpec : Spek({ 3 describe("PersonJson#toPerson") {
4 lateinit var json: PersonJson 5 6 beforeEachTest { 7 mockkStatic(“example.PersonJsonKt") 8 } 9 10 context("when sex is male”) { 11 beforeEachTest { 12 json = mockk<PersonJson>(“json1”).also { 13 every { it.toPerson() } returns mockk<Male>() 14 } 15 } 16 17 it("should return Male instance") { 18 assertTrue(json.toPerson() is Male) 19 } 20 } 21 … 22 afterEachTest { 23 unmockkStatic(“example.PersonJsonKt") 24 } 25 } 26 } ֤EFTDSJCFDPOUFYUϒϩοΫͷςετ͕Δલʹ ࣮ߦ͍ͨ͠ॲཧΛCFGPSF&BDI5FTUʹهड़ !42
1 @RunWith(JUnitPlatform::class) 2 object EntryFormMapperSpec : Spek({ 3 describe("PersonJson#toPerson") {
4 lateinit var json: PersonJson 5 6 beforeEachTest { 7 mockkStatic(“example.PersonJsonKt") 8 } 9 10 context("when sex is male”) { 11 beforeEachTest { 12 json = mockk<PersonJson>(“json1”).also { 13 every { it.toPerson() } returns mockk<Male>() 14 } 15 } 16 17 it("should return Male instance") { 18 assertTrue(json.toPerson() is Male) 19 } 20 } 21 … 22 afterEachTest { 23 unmockkStatic(“example.PersonJsonKt") 24 } 25 } 26 } JUϒϩοΫʹςετέʔεΛهड़ దٓΞαʔγϣϯϝιουΛͬͯͷνΣοΫ !43
1 @RunWith(JUnitPlatform::class) 2 object EntryFormMapperSpec : Spek({ 3 describe("PersonJson#toPerson") {
4 lateinit var json: PersonJson 5 6 beforeEachTest { 7 mockkStatic(“example.PersonJsonKt") 8 } 9 10 context("when sex is male”) { 11 beforeEachTest { 12 json = mockk<PersonJson>(“json1”).also { 13 every { it.toPerson() } returns mockk<Male>() 14 } 15 } 16 17 it("should return Male instance") { 18 assertTrue(json.toPerson() is Male) 19 } 20 } 21 … 22 afterEachTest { 23 unmockkStatic(“example.PersonJsonKt") 24 } 25 } 26 } ֤EFTDSJCFDPOUFYUϒϩοΫͷςετ͕ͬͨޙʹ ࣮ߦ͍ͨ͠ॲཧΛBGUFS&BDI5FTUʹهड़ !44
+6OJUͱͷൺֱ w +6OJU w BOOPUBUJPOͰςετϝιουΛࢦఆϘΠϥʔϓϨʔτͷ૿Ճ w 4QFL w EFTDSJCFDPOUFYUJU༷ॻͱͯ͠ಡΈ͍͢ɺৼΔ͍ۦಈ w
BOOPUBUJPOෆཁ༨ܭͳهड़͕গͳ͘ɺ࡞ۀίετΛݮΒͤΔ w +6OJUલఏɺ3PCPMFDUSJDະରԠ྆ํͱղܾ͞ΕͨΒ˕ !45
,PUMJOϑϨϯυϦʔͳ 6OJU5FTUڥͷߏங !46
ڥߏஙͷྲྀΕ ϞοΫϥΠϒϥϦಋೖ .PDL, ςετϑϨʔϜϫʔΫಋೖ 4QFL
ΧόϨοδܭଌϥΠϒϥϦಋೖ +B$P$P !47
.PDL,ಋೖ w EFQFOEFODJFTʹߦՃɺ͜Ε͚ͩ 1 testImplementation “io.mockk:mockk:1.9” 2 3 // or
4 5 androidTestImplementation “io.mockk:mockk:1.9” !48
4QFLಋೖ w Δ͖͜ͱ͕͍͔ͭ͋͘Δ ϋϚΓϙΠϯτ w "OESPJEϓϩδΣΫτ্Ͱ+6OJUΛಈ͔͢(SBEMF1MVHJOಋೖ w 4QFLͷςετΤϯδϯΛ(SBEMFʹࢦఆ w
+6OJUͷςετͱͷڞଘઃఆ !49
4QFLಋೖ w BOESPJEKVOJUͷಋೖ w (JUIVCNBOOPEFSNBVTBOESPJEKVOJU buile.gradle 1 buildscript { 2
dependencies { 3 classpath "de.mannodermaus.gradle.plugins:android-junit5:1.3.2.0" 4 } 5 } app/build.gradle 1 apply plugin: “de.mannodermaus.android-junit5” !50
4QFLಋೖ w EFQFOEFODJFTʹ4QFLͷґଘؔՃ w WWͰจ๏͕େ͖͘มΘ͍ͬͯΔ app/build.gradle 1 repositories { 2
… 3 maven { url "https://dl.bintray.com/spekframework/spek-dev" } 4 } 5 6 dependencies { 7 testImplementation “org.spekframework.spek2:spek-dsl-jvm:2.0.0” 8 testImplementation “org.spekframework.spek2:spek-runner-junit5:2.0.0“ 9 // JUnit4ͷςετΛ࣮ߦ͢ΔͨΊʹඞཁ 10 testImplementation “junit:junit:4.12” 11 testImplementation “org.junit.vintage:junit-vintage-engine:5.2.0” 12 } !51
4QFLಋೖ w +6OJU1MBUGPSNʹʮTQFLʯʮKVOJUWJOUBHFʯΛՃ w KVOJUWJOUBHF+6OJU্Ͱ+6OJUͷςετΛ࣮ߦ͢ΔΤϯδϯ w 3PCPMFDUSJDΛ͍͍ͨ߹ɺ+6OJUͰॻ͘ඞཁ͕͋ΔͨΊ 1 android {
2 testOptions.junitPlatform { 3 filters { 4 engine.include “spek2”, “junit-vintage" 5 } 6 } 7 } !52
4QFLಋೖ ʮTQFLʯͱʮ+6OJU7JOUBHFʯͰςετ͕࣮ߦ͞ΕΔ w ʮTQFLʯͱʮ+6OJU7JOUBHFʯͰςετ͕࣮ߦ͞ΕΔ !53
+B$P$Pಋೖ w +B$P$PίʔυΧόϨοδܭଌϥΠϒϥϦ w +BWB w ίʔυΧόϨοδΛ9.-ɾ)5.-ܗࣜͰՄࢹԽ w .PDL,ɾ4QFLͲͪΒରԠ !54
)5.-ग़ྗ !55
$PEFDPWͱ࿈ܞ !56
$PEFDPWͱ࿈ܞ !57
+B$P$Pಋೖ w (SBEMFλεΫͷ࡞͕ඞཁ w ΧόϨοδϨϙʔτ࡞λεΫ w ΧόϨοδϨϙʔτूܭλεΫ w ϚϧνϞδϡʔϧϓϩδΣΫτͰඞਢ !58
ΧόϨοδϨϙʔτ࡞ w Δ͜ͱ w ΧόϨοδରϑΝΠϧͷࢦఆ w ϨϙʔτϑΝΠϧͷग़ྗܗࣜͷࢦఆ w ϨϙʔτϑΝΠϧͷग़ྗઌͷࢦఆ w
Ϩϙʔτ࡞Λ6OJU5FTUͷλεΫ͕ऴΘͬͨޙʹߦ͏ !59
1 apply plugin: “jacoco” 2 3 jacoco { 4 toolVersion
= “0.8.1” 5 } 6 7 def excludeFiles = [ /* ूܭ͔Βআ֎͍ͨ͠ϑΝΠϧ໊ */ ] 8 9 task jacocoTestReports(type: JacocoReport, dependsOn: “testDebugUnitTest”) { 10 group = “Reporting” 11 description = “Generate Jacoco coverage reports for the build.” 12 13 classDirectories = files( 14 fileTree( 15 dir: “${buildDir}/tmp/kotlin-classes/debug”, 16 excludes: excludeFiles 17 ) 18 ) 19 sourceDirectories = files([“${buildDir}/src/main/kotlin”]) 20 21 reports { 22 xml.enabled true 23 html.enabled true 24 csv.enabled false 25 26 xml.destination file(“${buildDir}/reports/jacoco/report.xml”) 27 html.destination file(“${buildDir}/reports/jacoco/html”) 28 } 29 } !60
9 task jacocoTestReports(type: JacocoReport, dependsOn: “testDebugUnitTest”) { 10 group =
“Reporting” 11 description = “Generate Jacoco coverage reports for the build.” 12 13 classDirectories = files( 14 fileTree( 15 dir: “${buildDir}/tmp/kotlin-classes/debug”, 16 excludes: excludeFiles 17 ) 18 ) 19 sourceDirectories = files([“${buildDir}/src/main/kotlin”]) 20 21 reports { 22 xml.enabled true 23 html.enabled true 24 csv.enabled false 25 26 xml.destination file(“${buildDir}/reports/jacoco/report.xml”) 27 html.destination file(“${buildDir}/reports/jacoco/html”) 28 } 29 } UZQFΛʮ+BDPDP3FQPSUʯʹࢦఆ͠ɺλεΫΛએݴ EFQFOET0OʹʮUFTU%FCVH6OJU5FTUʯΛࢦఆ͠ɺ 6OJU5FTUλεΫͷޙʹϨϙʔτ࡞͕࣮ߦ͞ΕΔΑ͏ʹ͢Δ !61
9 task jacocoTestReports(type: JacocoReport, dependsOn: “testDebugUnitTest”) { 10 group =
“Reporting” 11 description = “Generate Jacoco coverage reports for the build.” 12 13 classDirectories = files( 14 fileTree( 15 dir: “${buildDir}/tmp/kotlin-classes/debug”, 16 excludes: excludeFiles 17 ) 18 ) 19 sourceDirectories = files([“${buildDir}/src/main/kotlin”]) 20 21 reports { 22 xml.enabled true 23 html.enabled true 24 csv.enabled false 25 26 xml.destination file(“${buildDir}/reports/jacoco/report.xml”) 27 html.destination file(“${buildDir}/reports/jacoco/html”) 28 } 29 } ΧόϨοδରͷϑΝΠϧɾΫϥεΛࢦఆ ˞আ֎͢ΔϑΝΠϧͷྫ 3DMBTT #VJME$POpH 5FTU %BHHFS $PNQPOFOU .PEVMF !62
9 task jacocoTestReports(type: JacocoReport, dependsOn: “testDebugUnitTest”) { 10 group =
“Reporting” 11 description = “Generate Jacoco coverage reports for the build.” 12 13 classDirectories = files( 14 fileTree( 15 dir: “${buildDir}/tmp/kotlin-classes/debug”, 16 excludes: excludeFiles 17 ) 18 ) 19 sourceDirectories = files([“${buildDir}/src/main/kotlin”]) 20 21 reports { 22 xml.enabled true 23 html.enabled true 24 csv.enabled false 25 26 xml.destination file(“${buildDir}/reports/jacoco/report.xml”) 27 html.destination file(“${buildDir}/reports/jacoco/html”) 28 } 29 } Ϩϙʔτͷग़ྗܗࣜɾग़ྗઌͷࢦఆ !63
ΧόϨοδϨϙʔτूܭ w ϞδϡʔϧຖʹΧόϨοδܭଌ͕࣮ߦ͞ΕΔ w ܭଌ݁ՌͷϑΝΠϧ͕ࢄͯ͠͠·͏ˠूܭλεΫͰ·ͱΊΔ w Δ͜ͱ w ΧόϨοδܭଌΛ6OJU5FTUޙʹ࣮ߦ͢ΔΑ͏ࢦఆ Ϟδϡʔϧຖ
w ֤ϞδϡʔϧͷΧόϨοδରϑΝΠϧɺΧόϨοδܭଌ݁ՌΛ·ͱΊɺ Ϩϙʔτ࡞λεΫʹ͢ !64
1 task jacocoMergeReports(type: JacocoMerge) { 2 group = "Reporting" 3
description = "Merge all JaCoCo reports from projects into one." 4 5 gradle.afterProject { p, state -> 6 if (p.rootProject != p && p.plugins.hasPlugin("jacoco")) { 7 dependsOn "${p.path}:testDebugUnitTest" 8 executionData "${p.buildDir}/jacoco/testDebugUnitTest.exec" 9 } 10 } 11 } !65
1 task jacocoMergeReports(type: JacocoMerge) { 2 group = "Reporting" 3
description = "Merge all JaCoCo reports from projects into one." 4 5 gradle.afterProject { p, state -> 6 if (p.rootProject != p && p.plugins.hasPlugin("jacoco")) { 7 dependsOn "${p.path}:testDebugUnitTest" 8 executionData "${p.buildDir}/jacoco/testDebugUnitTest.exec" 9 } 10 } 11 } UZQFΛʮ+BDPDP.FSHFʯʹࢦఆ͠ɺλεΫΛએݴ !66
1 task jacocoMergeReports(type: JacocoMerge) { 2 group = "Reporting" 3
description = "Merge all JaCoCo reports from projects into one." 4 5 gradle.afterProject { p, state -> 6 if (p.rootProject != p && p.plugins.hasPlugin("jacoco")) { 7 dependsOn "${p.path}:testDebugUnitTest" 8 executionData "${p.buildDir}/jacoco/testDebugUnitTest.exec" 9 } 10 } 11 } ֤Ϟδϡʔϧͷ6OJU5FTUλεΫऴྃޙʹ ϨϙʔτܭଌλεΫ͕࣮ߦ͞ΕΔΑ͏ʹࢦఆ !67
1 task jacocoMergeReports(type: JacocoMerge) { 2 group = "Reporting" 3
description = "Merge all JaCoCo reports from projects into one." 4 5 gradle.afterProject { p, state -> 6 if (p.rootProject != p && p.plugins.hasPlugin("jacoco")) { 7 dependsOn "${p.path}:testDebugUnitTest" 8 executionData "${p.buildDir}/jacoco/testDebugUnitTest.exec" 9 } 10 } 11 } ֤ϞδϡʔϧͷΧόϨοδܭଌ݁ՌͷύεΛ͢ ˠͨ͠ܭଌ݁Ռ͕࠷ޙʹϚʔδ͞ΕΔ !68
1 task jacocoMergeReports(type: JacocoMerge) { 2 group = "Reporting" 3
description = "Merge all JaCoCo reports from projects into one." 4 5 gradle.afterProject { p, state -> 6 if (p.rootProject != p && p.plugins.hasPlugin("jacoco")) { 7 dependsOn "${p.path}:testDebugUnitTest" 8 executionData "${p.buildDir}/jacoco/testDebugUnitTest.exec" 9 } 10 } 11 } ʮBQQMZQMVHJOKBDPDPʯ͕༗ޮͳϞδϡʔϧͷΈ࣮ߦ !69
1 task jacocoTestReports(type: JacocoReport, dependsOn: "jacocoMergeReports") { 2 … 3
4 // [before] 5 // classDirectories = … 6 // sourceDirectories = … 7 8 executionData jacocoMergeReports.destinationFile 9 10 sourceDirectories = files() 11 classDirectories = files() 12 13 gradle.afterProject { p, state -> 14 if (p.rootProject != p && p.plugins.hasPlugin("jacoco")) { 15 classDirectories = classDirectories + files([ 16 fileTree( 17 dir: “${p.buildDir}/tmp/kotlin-classes/release", 18 excludes: excludeFiles 19 ) 20 ]) 21 sourceDirectories = sourceDirectories + files([ 22 "${p.buildDir}/src/main/kotlin" 23 ]) 24 } 25 26 } 27 28 … 29 } Ұ෦ൈਮ λεΫϑΝΠϧશମIUUQCJUMZ)DE(9+ !70
1 task jacocoTestReports(type: JacocoReport, dependsOn: "jacocoMergeReports") { 2 … 3
4 // [before] 5 // classDirectories = … 6 // sourceDirectories = … 7 8 executionData jacocoMergeReports.destinationFile 9 10 sourceDirectories = files() 11 classDirectories = files() 12 13 gradle.afterProject { p, state -> 14 if (p.rootProject != p && p.plugins.hasPlugin("jacoco")) { 15 classDirectories = classDirectories + files([ 16 fileTree( 17 dir: “${p.buildDir}/tmp/kotlin-classes/release", 18 excludes: excludeFiles 19 ) 20 ]) 21 sourceDirectories = sourceDirectories + files([ 22 "${p.buildDir}/src/main/kotlin" ΧόϨοδूܭλεΫͷޙʹϨϙʔτ࡞͕ ࣮ߦ͞ΕΔΑ͏ʹ͢Δ !71
ΧόϨοδूܭλεΫͷޙʹϨϙʔτ࡞͕ ࣮ߦ͞ΕΔΑ͏ʹ͢Δ 5 // classDirectories = … 6 // sourceDirectories
= … 7 8 executionData jacocoMergeReports.destinationFile 9 10 sourceDirectories = files() 11 classDirectories = files() 12 13 gradle.afterProject { p, state -> 14 if (p.rootProject != p && p.plugins.hasPlugin("jacoco")) { 15 classDirectories = classDirectories + files([ 16 fileTree( 17 dir: “${p.buildDir}/tmp/kotlin-classes/release", 18 excludes: excludeFiles 19 ) 20 ]) 21 sourceDirectories = sourceDirectories + files([ 22 "${p.buildDir}/src/main/kotlin" 23 ]) 24 } 25 26 } 27 ΧόϨοδूܭλεΫʹ͞Ε֤ͨϞδϡʔϧͷ ܭଌ݁ՌͷύεΛ͢ !72
ΧόϨοδूܭλεΫͷޙʹϨϙʔτ࡞͕ ࣮ߦ͞ΕΔΑ͏ʹ͢Δ 5 // classDirectories = … 6 // sourceDirectories
= … 7 8 executionData jacocoMergeReports.destinationFile 9 10 sourceDirectories = files() 11 classDirectories = files() 12 13 gradle.afterProject { p, state -> 14 if (p.rootProject != p && p.plugins.hasPlugin("jacoco")) { 15 classDirectories = classDirectories + files([ 16 fileTree( 17 dir: “${p.buildDir}/tmp/kotlin-classes/release", 18 excludes: excludeFiles 19 ) 20 ]) 21 sourceDirectories = sourceDirectories + files([ 22 "${p.buildDir}/src/main/kotlin" 23 ]) 24 } 25 26 } 27 ֤Ϟδϡʔϧʹࢄ͍ͯ͠Δ ΧόϨοδରΫϥεɾϑΝΠϧΛࢦఆ !73
λεΫ࣮ߦ ΧόϨοδϨϙʔτ͕ग़ྗ )5.-ϑΝΠϧͰ֬ೝ 9.-ϑΝΠϧΛ֤छΧόϨοδऔಘαʔϏεʹQPTU $ ./gradlew jacocoTestReport ڥߏஙऴྃʂ͓ർΕ༷Ͱ͢ʂ !74
·ͱΊ w .PDL, w ,PUMJOͰඞཁͱ͞ΕΔϞοΫ༻ϝιουΛ͘Χόʔ w ಋೖ؆୯ɺطଘͷςετίʔυͷதͰؾܰʹѻ͑Δ w 4QFL w
༨ܭͳهड़͕ݮΓɺ༷ॻͱͯ͠ݟ͍͢ܗͰॻ͚Δ w 3PCPMFDUSJDରԠɺ"OESPJEϓϩδΣΫτͰͷ+6OJUରԠ͕ͨΕΔ !75
·ͱΊ ,PUMJOϑϨϯυϦʔͳ6OJU5FTUڥͰ ܧଓੑͷߴ͍ςετΛ࣮ݱ͠·͠ΐ͏ʂ !76
એ גࣜձࣾ#FBS5BJMɹΤϯδχΞืूதʂ ৬छ w "OESPJEΤϯδχΞ ,PUMJO+BWB w J04ΤϯδχΞ 4XJGU0CKFDUJWF$
w 8FCΤϯδχΞ 3VCZPO3BJMT3FBDU3FEVY ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠N @@ N ձࣾհࢿྉIUUQCJUMZ".V"* !77