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
5k
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で書きたい! -ネイティブアプリの「あの機能」を私たちはどう作るか-
subroh0508
1
1.2k
登壇の心理的ハードルをコントロールする技術
subroh0508
2
380
テストコードを書きながらCompose Multiplatformを乗りこなす
subroh0508
0
390
自己効力感を二次元アイドル作品から得ながら社会人としての成長を超加速させる
subroh0508
2
530
担当アイドルを応援する傘を作ろう! (として失敗した話)
subroh0508
0
410
buildSrc/Composite Buildで必要なバージョン情報も Version Catalogから参照したい!
subroh0508
0
1.2k
フロントエンドもJetpack Composeで書きたい! -Compose for WebはモダンWebアプリケーションの夢を見るか?-
subroh0508
0
800
det(A-λE) = 0 〜我々はなぜ三峰結華に惹かれるのか〜
subroh0508
0
140
J( 'ー`)し「Jetpack Composeって、Desktopアプリも作れるのよ」
subroh0508
0
110
Other Decks in Technology
See All in Technology
JBUG岡山 #6 WordCamp男木島の チームビルディング
takeshifurusato
0
150
推薦システムを本番導入する上で一番優先すべきだったこと~NewsPicks記事推薦機能の改善事例を元に~
morinota
0
130
Docker互換のセキュアなコンテナ実行環境「Podman」超入門
devops_vtj
6
3.2k
技術負債による事業の失敗はなぜ起こるのか / Why do business failures due to technical debt occur?
i35_267
0
190
セキュリティ研修 Day1【MIXI 24新卒技術研修】
mixi_engineers
PRO
0
160
20240724_cm_odyssey_hibiyatech
hiashisan
0
110
スタートアップにおける組織設計とスクラムの長期戦略 / Scrum Fest Kanazawa 2024
yoshikiiida
13
3.6k
エンジニアの生存戦略 〜クラウド潮流の経験から紐解く技術トレンドのメカニズムと乗りこなし方〜
shimy
9
1.9k
20240725 LLMによるDXのビジョンと、今何からやるべきか @Azure OpenAI Service Dev Day
nrryuya
3
1.2k
Scaling Technical Excellence at 104: Evolution in AWS and Developer Empowerment
scotthsieh825
1
160
地理情報とAPIのトレンド
nagix
0
160
エンジニア向け会社紹介資料
caddi_eng
14
230k
Featured
See All Featured
How to Ace a Technical Interview
jacobian
274
23k
Designing the Hi-DPI Web
ddemaree
276
34k
Writing Fast Ruby
sferik
623
60k
Fashionably flexible responsive web design (full day workshop)
malarkey
399
65k
Docker and Python
trallard
37
2.9k
The Invisible Customer
myddelton
117
13k
Done Done
chrislema
179
15k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
44
4.7k
GraphQLとの向き合い方2022年版
quramy
36
13k
Building Your Own Lightsaber
phodgson
101
5.9k
A Tale of Four Properties
chriscoyier
155
22k
10 Git Anti Patterns You Should be Aware of
lemiorhan
652
58k
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