Slide 1

Slide 1 text

4QFL.PDL,+B$P$PͰΠέͯΔ 6OJU5FTU؀ڥΛखʹೖΕΖʂ %SPJE,BJHJ%BZ3PPNʙ ʹ͜͠Γ͞ͿΖʙ!TVCSPI@ !1

Slide 2

Slide 2 text

ࣗݾ঺հ w ʹ͜͠Γ͞ͿΖʙ ຊ໊ࡔ্੖৴ w ౦ژ౎ɾҏ౾େౡग़਎ w גࣜձࣾ#FBS5BJM w "OESPJEΤϯδχΞ ,PUMJO+BWB w 8FCΤϯδχΞ 3BJMT3FBDU !2

Slide 3

Slide 3 text

࢓ࣄ w %S8BMMFU ,PUMJOҠߦ࡞ۀதʜ !3

Slide 4

Slide 4 text

࢓ࣄ w %Sܦඅਫ਼ࢉ ϑϧ,PUMJOʂ .PDL,4QFL ಋೖࡁΈʂ !4

Slide 5

Slide 5 text

໨࣍ ͜Ε·Ͱͷ6OJU5FTU ,PUMJO੡ϑϨʔϜϫʔΫϥΠϒϥϦͷ঺հ w .PDL,ϞοΫϥΠϒϥϦ w 4QFLςετϑϨʔϜϫʔΫ ,PUMJOϑϨϯυϦʔͳ6OJU5FTU؀ڥͷߏங w .PDL,ɾ4QFLͷಋೖ w +B$P$PΛ࢖ͬͨίʔυΧόϨοδࢉग़ !5

Slide 6

Slide 6 text

ର৅ w ॳΊͯ"OESPJE։ൃͰ6OJU5FTUΛॻ͜͏ͱ͍ͯ͠Δਓ w +BWB੡ϥΠϒϥϦͰͷςετʹർΕͨਓ !6

Slide 7

Slide 7 text

͜Ε·Ͱͷ6OJU5FTU !7

Slide 8

Slide 8 text

,PUMJOਖ਼ࣜαϙʔτલ w .PDLJUPϞοΫϥΠϒϥϦ w WSFMFBTFʙ೥ࠒ w ཉ͍͠ػೳ͸େମἧ͍ͬͯΔ w ৘ใ͕๛෋ w ,PUMJO༻ϥούʔϥΠϒϥϦ΋ΞϦ w .PDLJUP,PUMJO ೥݄ʙ !8

Slide 9

Slide 9 text

w +6OJUςετϑϨʔϜϫʔΫ w "OESPJEϓϩδΣΫτͰͷσϑΥϧτͷϑϨʔϜϫʔΫ w BOOPUBUJPOͰςετϝιουΛࢦఆ ,PUMJOਖ਼ࣜαϙʔτલ !9

Slide 10

Slide 10 text

,PUMJOਖ਼ࣜαϙʔτલ w ϞοΫϥΠϒϥϦˠ.PDLJUP w ϢχοτςετϑϨʔϜϫʔΫˠ+6OJU w ͲͪΒ΋+BWB੡ w ,PUMJOରԠ͕ෆे෼ w ,PUMJOͷݴޠ࢓༷Λ׆͔͖͠Εͳ͍ !10

Slide 11

Slide 11 text

,PUMJOਖ਼ࣜαϙʔτ !(PPHMF*0 !11

Slide 12

Slide 12 text

೥൒ޙʜ w ,PUMJO੡ϥΠϒϥϦϑϨʔϜϫʔΫ w ,PUMJOͷݴޠ࢓༷Λϑϧʹ׆͔ͤΔʂ w ʮ+BWB͔Βݟͨ࣌͜ͷίʔυͲ͏ͳΔΜ͚ͩͬʯˠগͳ͘ͳΔ w ςετॻ࣌͘ʹߟ͑Δ΂͖͜ͱ͕ݮΔˠܧଓੑͷ͋Δςετ w ҆৺ͯ͠࢖͑Δʂ w ৘ใ͕૿͖͑ͯͨʂ ,PUMJO੡ϥΠϒϥϦϑϨʔϜϫʔΫ ಋೖͷ޷ػʂ !12

Slide 13

Slide 13 text

,PUMJO੡ ϑϨʔϜϫʔΫϥΠϒϥϦͷ঺հ !13

Slide 14

Slide 14 text

.PDL, w ϞοΫϥΠϒϥϦ w ,PUMJOಠࣗͷݴޠ࢓༷Λ໢ཏ w 6OJU5FTU*OTUSVNFOUFE5FTU྆ରԠ w %SPJE,BJHJެࣜΞϓϦͰ΋ར༻ (JUIVCNPDLLNPDLL !14

Slide 15

Slide 15 text

Ͱ͖Δ͜ͱ w ελϒ w ϝιουͷฦΓ஋ͷࠩ͠ସ͑ w ϞοΫ w ϝιουͷҾ਺ɺݺ͹Εͨճ਺ɺฦΓ஋ͷݕূ w ϝιου಺ͷॲཧ͸ಈ࡞͠ͳ͍ w εύΠ w ϝιουͷҾ਺ɺݺ͹Εͨճ਺ɺฦΓ஋ͷݕূ w ϝιου಺ͷॲཧ͕࣮ࡍʹಈ࡞͢Δ ৄ͘͠͸ "OESPJEςετશॻͰʜ !15

Slide 16

Slide 16 text

NPDLL5ϝιου 1 val car = mockk() 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

Slide 17

Slide 17 text

NPDLL5ϝιου 1 val car = mockk() 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

Slide 18

Slide 18 text

NPDLL5ϝιου 1 val car = mockk() 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

Slide 19

Slide 19 text

NPDLL5ϝιου 1 val car = mockk() 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

Slide 20

Slide 20 text

NPDLL5ϝιου 1 val car = mockk() 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

Slide 21

Slide 21 text

TQZL5ϝιου 1 val car = spyk(Car()) 2 3 car.drive(Direction.NORTH) 4 5 verify { car.drive(Direction.NORTH) } 6 7 confirmVerified(car) !21

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

1 val car = mockk() 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

Slide 25

Slide 25 text

ϞοΫ΋εύΠ΋Ϛονϟʔ΋.PDLJUPʹ͋ΔΑʁʁʁ !25

Slide 26

Slide 26 text

.PDLJUPͷ໰୊఺ w 'JOBMͳΫϥεϝιου͕ϞοΫͰ͖ͳ͍ʂ w ,PUMJOͷΫϥεϝιου͸σϑΥϧτͰ'JOBM w ճආࡦLPUMJOBMMPQFOϓϥάΠϯ CZ+FU#SBJOT w BOOPUBUJPO͚ͭΔͱPQFOʹͯ͘͠ΕΔɹ ڧҾͩͳʜ w ςετͷͨΊʹຊ൪ίʔυ΁ͷ༨ܭͳهड़͕૿͑Δ !26

Slide 27

Slide 27 text

.PDLJUPͷ໰୊఺ w 4UBUJDͳϝιου͕ϞοΫͰ͖ͳ͍ʂ w ֦ுؔ਺ɹγϯάϧτϯΦϒδΣΫτ w ճආࡦ1PXFS.PDL w 4UBUJDϝιουɺQSJWBUFϝιουͷϞοΫ͕Ͱ͖Δ w Θ͟Θ͟ผϥΠϒϥϦΛಋೖ͢Δ͜ͱ͕ඍົ !27

Slide 28

Slide 28 text

.PDL,ͷڧΈ w ,PUMJOͷΫϥεϝιουΛ΄΅ԿͰ΋ϞοΫͰ͖ͯ͠·͏ʂ w 'JOBMͳΫϥεϝιου w γϯάϧτϯΦϒδΣΫτ w ֦ுؔ਺ w QSJWBUFϝιου w TVTQFOEϝιουFUD !28

Slide 29

Slide 29 text

γϯάϧτϯΦϒδΣΫτ 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

Slide 30

Slide 30 text

γϯάϧτϯΦϒδΣΫτ 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

Slide 31

Slide 31 text

γϯάϧτϯΦϒδΣΫτ 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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

ͦͷଞʹ΋༷ʑͳϝιου͕༻ҙ ˠ.PDL,ͷ3&"%.& IUUQTNPDLLJP Λࢀর !35

Slide 36

Slide 36 text

4QFL w ςετϑϨʔϜϫʔΫ w 3VCZͷ34QFDͱࣅͨจ๏ w +6OJU্Ͱಈ࡞ w 6OJU5FTUͷΈରԠ w 3PCPMFDUSJD΋ඇରԠ *TTVFʹ͸্͕͍ͬͯΔ w Wਖ਼ࣜϦϦʔε ೔લ (JUIVCTQFLGSBNFXPSLTQFL ˞3PPNʙͷ ɹηογϣϯͰ΋঺հ W !36

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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(“json1”).also { 13 every { it.toPerson() } returns mockk() 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

Slide 39

Slide 39 text

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(“json1”).also { 13 every { it.toPerson() } returns mockk() 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

Slide 40

Slide 40 text

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(“json1”).also { 13 every { it.toPerson() } returns mockk() 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

Slide 41

Slide 41 text

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(“json1”).also { 13 every { it.toPerson() } returns mockk() 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

Slide 42

Slide 42 text

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(“json1”).also { 13 every { it.toPerson() } returns mockk() 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

Slide 43

Slide 43 text

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(“json1”).also { 13 every { it.toPerson() } returns mockk() 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

Slide 44

Slide 44 text

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(“json1”).also { 13 every { it.toPerson() } returns mockk() 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

Slide 45

Slide 45 text

+6OJUͱͷൺֱ w +6OJU w BOOPUBUJPOͰςετϝιουΛࢦఆϘΠϥʔϓϨʔτͷ૿Ճ w 4QFL w EFTDSJCFDPOUFYUJU࢓༷ॻͱͯ͠ಡΈ΍͍͢ɺৼΔ෣͍ۦಈ w BOOPUBUJPOෆཁ༨ܭͳهड़͕গͳ͘ɺ࡞ۀίετΛݮΒͤΔ w +6OJUલఏɺ3PCPMFDUSJDະରԠ྆ํͱ΋ղܾ͞ΕͨΒ˕ !45

Slide 46

Slide 46 text

,PUMJOϑϨϯυϦʔͳ 6OJU5FTU؀ڥͷߏங !46

Slide 47

Slide 47 text

؀ڥߏஙͷྲྀΕ ϞοΫϥΠϒϥϦಋೖ .PDL, ςετϑϨʔϜϫʔΫಋೖ 4QFL ΧόϨοδܭଌϥΠϒϥϦಋೖ +B$P$P !47

Slide 48

Slide 48 text

.PDL,ಋೖ w EFQFOEFODJFTʹߦ௥Ճɺ͜Ε͚ͩ 1 testImplementation “io.mockk:mockk:1.9” 2 3 // or 4 5 androidTestImplementation “io.mockk:mockk:1.9” !48

Slide 49

Slide 49 text

4QFLಋೖ w ΍Δ΂͖͜ͱ͕͍͔ͭ͋͘Δ ϋϚΓϙΠϯτ w "OESPJEϓϩδΣΫτ্Ͱ+6OJUΛಈ͔͢(SBEMF1MVHJOಋೖ w 4QFLͷςετΤϯδϯΛ(SBEMFʹࢦఆ w +6OJUͷςετͱͷڞଘઃఆ !49

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

4QFLಋೖ ʮTQFLʯͱʮ+6OJU7JOUBHFʯͰςετ͕࣮ߦ͞ΕΔ w ʮTQFLʯͱʮ+6OJU7JOUBHFʯͰςετ͕࣮ߦ͞ΕΔ !53

Slide 54

Slide 54 text

+B$P$Pಋೖ w +B$P$PίʔυΧόϨοδܭଌϥΠϒϥϦ w +BWB੡ w ίʔυΧόϨοδΛ9.-ɾ)5.-ܗࣜͰՄࢹԽ w .PDL,ɾ4QFLͲͪΒ΋ରԠ !54

Slide 55

Slide 55 text

)5.-ग़ྗ !55

Slide 56

Slide 56 text

$PEFDPWͱ࿈ܞ !56

Slide 57

Slide 57 text

$PEFDPWͱ࿈ܞ !57

Slide 58

Slide 58 text

+B$P$Pಋೖ w (SBEMFλεΫͷ࡞੒͕ඞཁ w ΧόϨοδϨϙʔτ࡞੒λεΫ w ΧόϨοδϨϙʔτूܭλεΫ w ϚϧνϞδϡʔϧϓϩδΣΫτͰ͸ඞਢ !58

Slide 59

Slide 59 text

ΧόϨοδϨϙʔτ࡞੒ w ΍Δ͜ͱ w ΧόϨοδର৅ϑΝΠϧͷࢦఆ w ϨϙʔτϑΝΠϧͷग़ྗܗࣜͷࢦఆ w ϨϙʔτϑΝΠϧͷग़ྗઌͷࢦఆ w Ϩϙʔτ࡞੒Λ6OJU5FTUͷλεΫ͕ऴΘͬͨ௚ޙʹߦ͏ !59

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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$POpH5FTU %BHHFS$PNQPOFOU.PEVMF !62

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

ΧόϨοδϨϙʔτूܭ w ϞδϡʔϧຖʹΧόϨοδܭଌ͕࣮ߦ͞ΕΔ w ܭଌ݁ՌͷϑΝΠϧ͕෼ࢄͯ͠͠·͏ˠूܭλεΫͰ·ͱΊΔ w ΍Δ͜ͱ w ΧόϨοδܭଌΛ6OJU5FTUޙʹ࣮ߦ͢ΔΑ͏ࢦఆ Ϟδϡʔϧຖ w ֤ϞδϡʔϧͷΧόϨοδର৅ϑΝΠϧɺΧόϨοδܭଌ݁ՌΛ·ͱΊɺ Ϩϙʔτ࡞੒λεΫʹ౉͢ !64

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

ΧόϨοδूܭλεΫͷ௚ޙʹϨϙʔτ࡞੒͕ ࣮ߦ͞ΕΔΑ͏ʹ͢Δ 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

Slide 73

Slide 73 text

ΧόϨοδूܭλεΫͷ௚ޙʹϨϙʔτ࡞੒͕ ࣮ߦ͞ΕΔΑ͏ʹ͢Δ 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

Slide 74

Slide 74 text

λεΫ࣮ߦ ΧόϨοδϨϙʔτ͕ग़ྗ )5.-ϑΝΠϧͰ֬ೝ 9.-ϑΝΠϧΛ֤छΧόϨοδऔಘαʔϏεʹQPTU $ ./gradlew jacocoTestReport ؀ڥߏஙऴྃʂ͓ർΕ༷Ͱ͢ʂ !74

Slide 75

Slide 75 text

·ͱΊ w .PDL, w ,PUMJOͰඞཁͱ͞ΕΔϞοΫ༻ϝιουΛ޿͘Χόʔ w ಋೖ΋؆୯ɺطଘͷςετίʔυͷதͰ΋ؾܰʹѻ͑Δ w 4QFL w ༨ܭͳهड़͕ݮΓɺ࢓༷ॻͱͯ͠΋ݟ΍͍͢ܗͰॻ͚Δ w 3PCPMFDUSJDରԠɺ"OESPJEϓϩδΣΫτͰͷ+6OJUରԠ͕଴ͨΕΔ !75

Slide 76

Slide 76 text

·ͱΊ ,PUMJOϑϨϯυϦʔͳ6OJU5FTU؀ڥͰ ܧଓੑͷߴ͍ςετΛ࣮ݱ͠·͠ΐ͏ʂ !76

Slide 77

Slide 77 text

એ఻ גࣜձࣾ#FBS5BJMɹΤϯδχΞืूதʂ ৬छ w "OESPJEΤϯδχΞ ,PUMJO+BWB w J04ΤϯδχΞ 4XJGU0CKFDUJWF$ w 8FCΤϯδχΞ 3VCZPO3BJMT3FBDU3FEVY ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠N @@ N ձࣾ঺հࢿྉIUUQCJUMZ".V"* !77