Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Spek2+MockK+JaCoCoでイケてる Unit Test環境を手に入れろ!

subroh_0508
February 08, 2019

Spek2+MockK+JaCoCoでイケてる Unit Test環境を手に入れろ!

DroidKaigi 2019 - Day2 Room1 11:20〜
「Spek2+MockK+JaCoCoでイケてる Unit Test環境を手に入れろ!」のセッション資料です

subroh_0508

February 08, 2019
Tweet

More Decks by subroh_0508

Other Decks in Technology

Transcript

  1. ໨࣍  ͜Ε·Ͱͷ6OJU5FTU  ,PUMJO੡ϑϨʔϜϫʔΫϥΠϒϥϦͷ঺հ w .PDL,ϞοΫϥΠϒϥϦ w 4QFLςετϑϨʔϜϫʔΫ 

    ,PUMJOϑϨϯυϦʔͳ6OJU5FTU؀ڥͷߏங w .PDL,ɾ4QFLͷಋೖ w +B$P$PΛ࢖ͬͨίʔυΧόϨοδࢉग़ !5
  2. Ͱ͖Δ͜ͱ w ελϒ w ϝιουͷฦΓ஋ͷࠩ͠ସ͑ w ϞοΫ w ϝιουͷҾ਺ɺݺ͹Εͨճ਺ɺฦΓ஋ͷݕূ w

    ϝιου಺ͷॲཧ͸ಈ࡞͠ͳ͍ w εύΠ w ϝιουͷҾ਺ɺݺ͹Εͨճ਺ɺฦΓ஋ͷݕূ w ϝιου಺ͷॲཧ͕࣮ࡍʹಈ࡞͢Δ ৄ͘͠͸ "OESPJEςετશॻͰʜ !15
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. TQZL5ϝιου 1 val car = spyk(Car()) 2 3 car.drive(Direction.NORTH) 4

    5 verify { car.drive(Direction.NORTH) } 6 7 confirmVerified(car) !21
  9. 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
  10. 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
  11. 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
  12. .PDLJUPͷ໰୊఺  w 'JOBMͳΫϥεϝιου͕ϞοΫͰ͖ͳ͍ʂ w ,PUMJOͷΫϥεϝιου͸σϑΥϧτͰ'JOBM w ճආࡦLPUMJOBMMPQFOϓϥάΠϯ CZ+FU#SBJOT 

    w BOOPUBUJPO͚ͭΔͱPQFOʹͯ͘͠ΕΔɹ ڧҾͩͳʜ  w ςετͷͨΊʹຊ൪ίʔυ΁ͷ༨ܭͳهड़͕૿͑Δ !26
  13. γϯάϧτϯΦϒδΣΫτ 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
  14. γϯάϧτϯΦϒδΣΫτ 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
  15. γϯάϧτϯΦϒδΣΫτ 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
  16. 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
  17. 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
  18. 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
  19. 4QFL w ςετϑϨʔϜϫʔΫ w 3VCZͷ34QFDͱࣅͨจ๏ w +6OJU্Ͱಈ࡞ w 6OJU5FTUͷΈରԠ w

    3PCPMFDUSJD΋ඇରԠ *TTVFʹ͸্͕͍ͬͯΔ  w Wਖ਼ࣜϦϦʔε ೔લ (JUIVCTQFLGSBNFXPSLTQFL ˞3PPNʙͷ ɹηογϣϯͰ΋঺հ W !36
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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
  28. +6OJUͱͷൺֱ w +6OJU w BOOPUBUJPOͰςετϝιουΛࢦఆϘΠϥʔϓϨʔτͷ૿Ճ w 4QFL w EFTDSJCFDPOUFYUJU࢓༷ॻͱͯ͠ಡΈ΍͍͢ɺৼΔ෣͍ۦಈ w

    BOOPUBUJPOෆཁ༨ܭͳهड़͕গͳ͘ɺ࡞ۀίετΛݮΒͤΔ w +6OJUલఏɺ3PCPMFDUSJDະରԠ྆ํͱ΋ղܾ͞ΕͨΒ˕ !45
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. 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
  36. 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
  37. 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
  38. 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
  39. 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
  40. 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
  41. 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
  42. ΧόϨοδूܭλεΫͷ௚ޙʹϨϙʔτ࡞੒͕ ࣮ߦ͞ΕΔΑ͏ʹ͢Δ 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
  43. ΧόϨοδूܭλεΫͷ௚ޙʹϨϙʔτ࡞੒͕ ࣮ߦ͞ΕΔΑ͏ʹ͢Δ 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
  44. ·ͱΊ w .PDL, w ,PUMJOͰඞཁͱ͞ΕΔϞοΫ༻ϝιουΛ޿͘Χόʔ w ಋೖ΋؆୯ɺطଘͷςετίʔυͷதͰ΋ؾܰʹѻ͑Δ w 4QFL w

    ༨ܭͳهड़͕ݮΓɺ࢓༷ॻͱͯ͠΋ݟ΍͍͢ܗͰॻ͚Δ w 3PCPMFDUSJDରԠɺ"OESPJEϓϩδΣΫτͰͷ+6OJUରԠ͕଴ͨΕΔ !75
  45. એ఻ גࣜձࣾ#FBS5BJMɹΤϯδχΞืूதʂ ৬छ w "OESPJEΤϯδχΞ ,PUMJO+BWB  w J04ΤϯδχΞ 4XJGU0CKFDUJWF$

     w 8FCΤϯδχΞ 3VCZPO3BJMT3FBDU3FEVY  ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠N @@ N ձࣾ঺հࢿྉIUUQCJUMZ".V"* !77