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.2k
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 for Webでポートフォリオサイトを作る
subroh0508
1
68
あらゆるアプリをCompose Multiplatformで書きたい! -ネイティブアプリの「あの機能」を私たちはどう作るか-
subroh0508
1
2.2k
登壇の心理的ハードルをコントロールする技術
subroh0508
2
490
テストコードを書きながらCompose Multiplatformを乗りこなす
subroh0508
0
710
自己効力感を二次元アイドル作品から得ながら社会人としての成長を超加速させる
subroh0508
2
630
担当アイドルを応援する傘を作ろう! (として失敗した話)
subroh0508
0
580
buildSrc/Composite Buildで必要なバージョン情報も Version Catalogから参照したい!
subroh0508
0
1.4k
フロントエンドもJetpack Composeで書きたい! -Compose for WebはモダンWebアプリケーションの夢を見るか?-
subroh0508
0
890
det(A-λE) = 0 〜我々はなぜ三峰結華に惹かれるのか〜
subroh0508
0
190
Other Decks in Technology
See All in Technology
オープンソースAIとは何か? --「オープンソースAIの定義 v1.0」詳細解説
shujisado
9
1.1k
アジャイルチームがらしさを発揮するための目標づくり / Making the goal and enabling the team
kakehashi
3
130
VideoMamba: State Space Model for Efficient Video Understanding
chou500
0
190
DynamoDB でスロットリングが発生したとき/when_throttling_occurs_in_dynamodb_short
emiki
0
250
適材適所の技術選定 〜GraphQL・REST API・tRPC〜 / Optimal Technology Selection
kakehashi
1
690
エンジニア人生の拡張性を高める 「探索型キャリア設計」の提案
tenshoku_draft
1
130
Python(PYNQ)がテーマのAMD主催のFPGAコンテストに参加してきた
iotengineer22
0
500
データプロダクトの定義からはじめる、データコントラクト駆動なデータ基盤
chanyou0311
2
330
飲食店データの分析事例とそれを支えるデータ基盤
kimujun
0
150
AIチャットボット開発への生成AI活用
ryomrt
0
170
Introduction to Works of ML Engineer in LY Corporation
lycorp_recruit_jp
0
140
RubyのWebアプリケーションを50倍速くする方法 / How to Make a Ruby Web Application 50 Times Faster
hogelog
3
950
Featured
See All Featured
The Art of Programming - Codeland 2020
erikaheidi
52
13k
Building Adaptive Systems
keathley
38
2.3k
BBQ
matthewcrist
85
9.3k
4 Signs Your Business is Dying
shpigford
180
21k
Writing Fast Ruby
sferik
627
61k
Designing for Performance
lara
604
68k
How GitHub (no longer) Works
holman
310
140k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
25
1.8k
RailsConf 2023
tenderlove
29
900
Imperfection Machines: The Place of Print at Facebook
scottboms
265
13k
The Pragmatic Product Professional
lauravandoore
31
6.3k
What's in a price? How to price your products and services
michaelherold
243
12k
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