Slide 1

Slide 1 text

4QFLͰ6OJU5FTUΛॻ͜͏ "ZBLP.PSJNPUP!NPSJJJNP

Slide 2

Slide 2 text

ʘ࠷ۙϦχϡʔΞϧ͠·ͨ͠ʗ "ZBLP.PSJNPUP %..DPNMJGFࣄۀ෦ ࣗݾ঺հ

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

4QFLΛ஌Γɺ ςετΛॻ͖͍ͨͱࢥͬͯ΋Β͏͜ͱ ΰʔϧ

Slide 5

Slide 5 text

ຊ೔͓࿩͢Δ͜ͱ 4QFLͱ͸ʁ 4QFLͰ6OJU5FTUΛॻ͍ͯΈΑ͏ Ԡ༻ฤ4QFLͰ-JWF%BUB΍3PPNͷ 6OJU5FTUΛॻ͍ͯΈΑ͏

Slide 6

Slide 6 text

ຊ೔͓࿩͢Δ͜ͱ 4QFLͱ͸ʁ 4QFLͰ6OJU5FTUΛॻ͍ͯΈΑ͏ Ԡ༻ฤ4QFLͰ-JWF%BUB΍3PPNͷ 6OJU5FTUΛॻ͍ͯΈΑ͏

Slide 7

Slide 7 text

ຊ೔͓࿩͢Δ͜ͱ 4QFLͱ͸ʁ 4QFLͰ6OJU5FTUΛॻ͍ͯΈΑ͏ Ԡ༻ฤ4QFLͰ-JWF%BUB΍3PPNͷ 6OJU5FTUΛॻ͍ͯΈΑ͏

Slide 8

Slide 8 text

ຊ೔͓࿩͢Δ͜ͱ 4QFLͱ͸ʁ 4QFLͰ6OJU5FTUΛॻ͍ͯΈΑ͏ Ԡ༻ฤ4QFLͰ-JWF%BUB΍3PPNͷ 6OJU5FTUΛॻ͍ͯΈΑ͏

Slide 9

Slide 9 text

4QFLͱ͸ʁ

Slide 10

Slide 10 text

Copyright (c) 2018, Spek Core Team and Contributors

Slide 11

Slide 11 text

l5IJTTUZMFJTJOTQJSFECZUFTUGSBNFXPSLTMJLF +BTNJOFBOE34QFDz IUUQTTQFLGSBNFXPSLPSHTQFDJpDBUJPO

Slide 12

Slide 12 text

l5IJTTUZMFJTJOTQJSFECZUFTUGSBNFXPSLTMJLF +BTNJOFBOE34QFDz IUUQTTQFLGSBNFXPSLPSHTQFDJpDBUJPO +BWB4DSJQU

Slide 13

Slide 13 text

l5IJTTUZMFJTJOTQJSFECZUFTUGSBNFXPSLTMJLF +BTNJOFBOE34QFDz IUUQTTQFLGSBNFXPSLPSHTQFDJpDBUJPO 3VCZ

Slide 14

Slide 14 text

l5IJTTUZMFJTJOTQJSFECZUFTUGSBNFXPSLTMJLF +BTNJOFBOE34QFDz IUUQTTQFLGSBNFXPSLPSHTQFDJpDBUJPO

Slide 15

Slide 15 text

5PEP$MBTT class Todo(val id: Int) { constructor(id: Int, label: String, complete: Boolean, deadline: Long) : this(id) { this.label = label this.complete = complete this.deadline = deadline } var label: String = "" private set var complete: Boolean = false private set var deadline: Long = System.currentTimeMillis() private set }

Slide 16

Slide 16 text

5PEP$MBTT fun checkComplete() { complete = true } fun unCheckComplete() { complete = false } ऴྃঢ়ଶͷ มߋ͕Ͱ͖ͨΓʜ

Slide 17

Slide 17 text

5PEP$MBTT fun addDayToDeadline(numOfDays: Int) { changeDeadline(numOfDays) } fun reduceDayToDeadline(numOfDays: Int) { changeDeadline(-numOfDays) } private fun changeDeadline(numOfDays: Int) { deadline = Calendar.getInstance(TimeZone.getDefault(), Locale.JAPAN).apply { timeInMillis = deadline add(Calendar.DATE, numOfDays) }.timeInMillis } ظݶͷมߋ͕ Ͱ͖ͨΓ͠·͢

Slide 18

Slide 18 text

+6OJU5PEP5FTU class TodoTest { private val now = System.currentTimeMillis() private lateinit var todo: Todo @Before fun setup() { todo = Todo(id = 1, label = "Write Unit Test with Spek", complete = false, deadline = now ) } @Test fun changeCompleteStatus_shouldBeTrue() { todo.checkComplete() assertTrue(todo.complete) } @Test fun changeCompleteStatus_shouldBeFalse() { todo.unCheckComplete() assertFalse(todo.complete) } @Test fun addDaysToDeadline_shouldBeThreeDaysLater() { val expect = Calendar.getInstance(TimeZone.getDefault(), Locale.JAPAN).apply { timeInMillis = now add(Calendar.DATE, 3) } todo.addDayToDeadline(3) assertEquals(expect.timeInMillis, todo.deadline) } @Test fun reduceDaysToDeadline_shouldBeThreeDaysAgo() { val expect = Calendar.getInstance(TimeZone.getDefault(), Locale.JAPAN).apply { timeInMillis = now add(Calendar.DATE, -3) } todo.reduceDayToDeadline(3) assertEquals(expect.timeInMillis, todo.deadline) } }

Slide 19

Slide 19 text

ݟͮΒ͍ϙΠϯτ @Test fun changeFinishStatus_shouldBeTrue() { todo.checkComplete() assert(todo.complete) }

Slide 20

Slide 20 text

ݟͮΒ͍ϙΠϯτ ϝιου໊΍ίϝϯτͰ ૝ఆΛද͢ඞཁ͕͋Δ @Test fun changeFinishStatus_shouldBeTrue() { todo.checkComplete() assert(todo.complete) }

Slide 21

Slide 21 text

ݟͮΒ͍ϙΠϯτ @Test fun changeFinishStatus_shouldBeTrue() { todo.checkComplete() assert(todo.complete) }

Slide 22

Slide 22 text

ݟͮΒ͍ϙΠϯτ @Test fun changeCompleteStatus_shouldBeTrue() { todo.checkComplete() assertTrue(todo.complete) } @Test fun changeCompleteStatus_shouldBeFalse() { todo.unCheckComplete() assertFalse(todo.complete) }

Slide 23

Slide 23 text

ݟͮΒ͍ϙΠϯτ @Test fun changeCompleteStatus_shouldBeTrue() { todo.checkComplete() assertTrue(todo.complete) } @Test fun changeCompleteStatus_shouldBeFalse() { todo.unCheckComplete() assertFalse(todo.complete) } @Test fun addDaysToDeadline_shouldBeThreeDaysLater() { val expect = Calendar.getInstance(TimeZone.getDefault(), Locale.JAPAN).apply { timeInMillis = now add(Calendar.DATE, 3) } todo.addDayToDeadline(3) assertEquals(expect.timeInMillis, todo.deadline) }

Slide 24

Slide 24 text

ݟͮΒ͍ϙΠϯτ @Test fun changeCompleteStatus_shouldBeTrue() { todo.checkComplete() assertTrue(todo.complete) } @Test fun changeCompleteStatus_shouldBeFalse() { todo.unCheckComplete() assertFalse(todo.complete) } @Test fun addDaysToDeadline_shouldBeThreeDaysLater() { val expect = Calendar.getInstance(TimeZone.getDefault(), Locale.JAPAN).apply { timeInMillis = now add(Calendar.DATE, 3) } todo.addDayToDeadline(3) assertEquals(expect.timeInMillis, todo.deadline) } @Test fun reduceDaysToDeadline_shouldBeThreeDaysAgo() { val expect = Calendar.getInstance(TimeZone.getDefault(), Locale.JAPAN).apply { timeInMillis = now add(Calendar.DATE, -3) } todo.reduceDayToDeadline(3) assertEquals(expect.timeInMillis, todo.deadline) } ΧςΰϥΠζɺ ֊૚ԽͰ͖ͳ͍

Slide 25

Slide 25 text

ݟͮΒ͍ϙΠϯτ @Test fun changeFinishStatus_shouldBeTrue() { todo.markFinished() assert(todo.finished) } @Test fun changeFinishStatus_shouldBeFalse() { todo.markUnFinished() assert(!todo.finished) } @Test fun addDaysToDeadline_shouldBeThreeDaysLater() { val expect = Calendar.getInstance(TimeZone.getDefault(), Locale.JAPAN).apply { timeInMillis = now add(Calendar.DATE, 3) } todo.addDayToDeadline(3) assertEquals(expect.timeInMillis, todo.deadline) } @Test fun reduceDaysToDeadline_shouldBeThreeDaysAgo() { val expect = Calendar.getInstance(TimeZone.getDefault(), Locale.JAPAN).apply { timeInMillis = now add(Calendar.DATE, -3) } todo.reduceDayToDeadline(3) assertEquals(expect.timeInMillis, todo.deadline) } ࣮ߦॱং͕อূ ͞Εͳ͍

Slide 26

Slide 26 text

4QFL5PEP5FTU class TodoFeature : Spek({ Feature("Todo") { val now = System.currentTimeMillis() lateinit var todo: Todo Scenario("complete status") { Given("the todo is not complete") { todo = Todo( id = 1, label = "", deadline = now, complete = false ) } When("complete the todo") { todo.checkComplete() } Then("the todo is complete") { assertTrue(todo.complete) } When("return the todo to incomplete") { todo.unCheckComplete() } Then("the todo is not complete") { assertTrue(!todo.complete) } } } })

Slide 27

Slide 27 text

ݟ΍͍͢ϙΠϯτ Scenario("complete status") { Given("the todo is not complete") { todo = Todo( id = 1, label = "", deadline = now, complete = false ) } When("complete the todo") { todo.checkComplete() } Then("the todo is complete") { assertTrue(todo.complete) } }

Slide 28

Slide 28 text

ݟ΍͍͢ϙΠϯτ Scenario("complete status") { Given("the todo is not complete") { todo = Todo( id = 1, label = "", deadline = now, complete = false ) } When("complete the todo") { todo.checkComplete() } Then("the todo is complete") { assertTrue(todo.complete) } } ૝ఆ͕ ॻ͖΍͍͢

Slide 29

Slide 29 text

ิ଍ Scenario("changing todo finish status") { Given("todo is not finish") { todo = Todo( id = 1, label = "Write Unit Test with Spek", finished = false, deadline = now ) } When("mark finish") { todo.markFinished() } Then("it should be true") { assertTrue(todo.finished) } } ૝ఆ͕ ॻ͖΍͍͢ ͪͳΈʹʜ͜ͷΑ͏ͳه๏͸(IFSLJOͱ͍͏ ςετهड़ݴޠϑΥʔϚοτ͕ݩʹͳ͍ͬͯ·͢

Slide 30

Slide 30 text

ิ଍ Scenario("changing todo finish status") { Given("todo is not finish") { todo = Todo( id = 1, label = "Write Unit Test with Spek", finished = false, deadline = now ) } When("mark finish") { todo.markFinished() } Then("it should be true") { assertTrue(todo.finished) } } ૝ఆ͕ ॻ͖΍͍͢ ͪͳΈʹʜ͜ͷΑ͏ͳه๏͸(IFSLJOͱ͍͏ ςετهड़ݴޠϑΥʔϚοτ͕ݩʹͳ͍ͬͯ·͢ ػೳ50%0 γφϦΦऴྃεςʔλε͕ਖ਼͘͠มԽ͢Δ લఏ50%0ͷऴྃεςʔλε͕ະ׬ྃ ΋͠50%0ͷऴྃεςʔλεΛ׬ྃʹͨ͠Β ͳΒ͹50%0ͷऴྃεςʔλε͕׬ྃʹͳΔ ೔ຊޠͰ΋͔͚ͯɺ ॻ͘ͱ͜Μͳײ͡ʹͳΔ

Slide 31

Slide 31 text

ݟ΍͍͢ϙΠϯτ class TodoFeature : Spek({ Feature("Todo") { Scenario(“complete status") { . . . ऴྃεςʔλεͷςετ } Scenario("deadline") { . . . క੾ͷςετ } Scenario(“label") { . . . ໊લͷςετ } } })

Slide 32

Slide 32 text

ݟ΍͍͢ϙΠϯτ class TodoFeature : Spek({ Feature("Todo") { Scenario(“complete status") { . . . ऴྃεςʔλεͷςετ } Scenario("deadline") { . . . క੾ͷςετ } Scenario(“label") { . . . ໊લͷςετ } } }) Θ͔Γ΍͘͢Χςΰ ϥΠζͰ͖Δ

Slide 33

Slide 33 text

class TodoFeature : Spek({ Feature("Todo") { Scenario("changing finish status") { . . . ऴྃεςʔλεͷςετ } Scenario("changing deadline") { . . . క੾มߋͷςετ } Scenario("changing label") { . . . ໊લมߋͷςετ } } }) ݟ΍͍͢ϙΠϯτ ࣮ߦॱং͕อূ͞ΕΔ

Slide 34

Slide 34 text

4QFLͰ6OJU5FTUΛ ॻ͍ͯΈΑ͏

Slide 35

Slide 35 text

ηοτΞοϓ https://spekframework.org/setup-android/

Slide 36

Slide 36 text

ηοτΞοϓ https://spekframework.org/setup-android/ ๨Εͣʹ΍ͬͯ΄͍͠ͷ͕…

Slide 37

Slide 37 text

ηοτΞοϓ https://spekframework.org/setup-android/ ๨Εͣʹ΍ͬͯ΄͍͠ͷ͕… #1 SpekͷAndroidStudioϓϥάΠϯΛೖΕ·͠ΐ͏

Slide 38

Slide 38 text

ηοτΞοϓ https://spekframework.org/setup-android/ ๨Εͣʹ΍ͬͯ΄͍͠ͷ͕… #1 SpekͷAndroidStudioϓϥάΠϯΛೖΕ·͠ΐ͏ #2 KotlinͷAndroidStudioϓϥάΠϯΛ1.3Ҏ্ʹΞοϓσʔτ͠· ͠ΐ͏

Slide 39

Slide 39 text

ηοτΞοϓ https://spekframework.org/setup-android/ ๨Εͣʹ΍ͬͯ΄͍͠ͷ͕… #1 SpekͷAndroidStudioϓϥάΠϯΛೖΕ·͠ΐ͏ #2 KotlinͷAndroidStudioϓϥάΠϯΛ1.3Ҏ্ʹΞοϓσʔτ͠· ͠ΐ͏ Yͩͱʜ✗

Slide 40

Slide 40 text

جຊͷॻ͖ํ class TodoFeature

Slide 41

Slide 41 text

جຊͷॻ͖ํ class TodoFeature : Spek({ })

Slide 42

Slide 42 text

جຊͷॻ͖ํ class TodoFeature : Spek({ Feature("Todo") { } })

Slide 43

Slide 43 text

جຊͷॻ͖ํ class TodoFeature : Spek({ Feature("Todo") { val now = System.currentTimeMillis() lateinit var todo: Todo } })

Slide 44

Slide 44 text

جຊͷॻ͖ํ class TodoFeature : Spek({ Feature("Todo") { val now = System.currentTimeMillis() val todo: Todo by lazy { Todo(id = 1, label = "Write Unit Test with Spek", complete = false, deadline = now) } } }) MB[Z΋"

Slide 45

Slide 45 text

جຊͷॻ͖ํ class TodoFeature : Spek({ Feature("Todo") { val now = System.currentTimeMillis() lateinit var todo: Todo Scenario("complete status") { } } })

Slide 46

Slide 46 text

جຊͷॻ͖ํ class TodoFeature : Spek({ Feature("Todo") { val now = System.currentTimeMillis() lateinit var todo: Todo Scenario("complete status") { Given(“the todo is not complete") { todo = Todo(id = 1, label = "Write Unit Test with Spek”, complete = false, deadline = now) } } } })

Slide 47

Slide 47 text

جຊͷॻ͖ํ class TodoFeature : Spek({ Feature("Todo") { val now = System.currentTimeMillis() lateinit var todo: Todo Scenario("complete status") { Given(“the todo is not complete") { todo = Todo(id = 1, label = "Write Unit Test with Spek", complete = false, deadline = now) } When(“complete the todo") { todo.checkComplete() } } } })

Slide 48

Slide 48 text

جຊͷॻ͖ํ class TodoFeature : Spek({ Feature("Todo") { val now = System.currentTimeMillis() lateinit var todo: Todo Scenario("complete status") { Given(“the todo is not complete") { todo = Todo(id = 1, label = "Write Unit Test with Spek", complete = false, deadline = now) } When(“complete the todo") { todo.checkComplete() } Then(“the todo is complete”) { assertTrue(todo.complete) } } } })

Slide 49

Slide 49 text

ϧʔϧ class TodoFeature : Spek({ Feature("xxx") { Scenario("xxx") { . . . xxxͷςετ } Scenario("yyy") { . . . yyyͷςετ } } Feature("zzz") { Scenario("zzz") { . . . zzzͷςετ } } }) ෳ਺ͷ'FBUVSFɺ'FBUVSF಺ͷ ෳ਺ͷγφϦΦʁ

Slide 50

Slide 50 text

ෳ਺ͷ'FBUVSFɺ'FBUVSF಺ͷ ෳ਺ͷγφϦΦʁ ϧʔϧ class TodoFeature : Spek({ Feature("xxx") { Scenario("xxx") { . . . xxxͷςετ } Scenario("yyy") { . . . yyyͷςετ } } Feature("zzz") { Scenario("zzz") { . . . zzzͷςετ } } }) "

Slide 51

Slide 51 text

ϧʔϧ class TodoFeature : Spek({ Feature(“xxx") { Scenario("xxx") { Scenario(“yyy") { Given("xxx") { Given(“yyy”) { } } } } } }) γφϦΦ΍(JWFO8IFO 5IFOͷωετʁ

Slide 52

Slide 52 text

γφϦΦ΍(JWFO8IFO 5IFOͷωετʁ ϧʔϧ class TodoFeature : Spek({ Feature(“xxx") { Scenario("xxx") { Scenario(“yyy") { Given("xxx") { Given(“yyy”) { } } } } } }) #

Slide 53

Slide 53 text

@Synonym(SynonymType.GROUP, prefix = "Feature: ") @Descriptions(Description(DescriptionLocation.VALUE_PARAMETER, 0)) fun GroupBody.Feature(description: String, body: FeatureBody.() -> Unit) { group("Feature: $description", defaultCachingMode = CachingMode.GROUP) { body(FeatureBody(this)) } }

Slide 54

Slide 54 text

@Synonym(SynonymType.GROUP, prefix = "Feature: ") @Descriptions(Description(DescriptionLocation.VALUE_PARAMETER, 0)) fun GroupBody.Feature(description: String, body: FeatureBody.() -> Unit) { group("Feature: $description", defaultCachingMode = CachingMode.GROUP) { body(FeatureBody(this)) } } ͜Ε

Slide 55

Slide 55 text

@Synonym(SynonymType.GROUP, prefix = "Feature: ") @Descriptions(Description(DescriptionLocation.VALUE_PARAMETER, 0)) fun GroupBody.Feature(description: String, body: FeatureBody.() -> Unit) { group("Feature: $description", defaultCachingMode = CachingMode.GROUP) { body(FeatureBody(this)) } } @SpekDsl class FeatureBody(val delegate: GroupBody): LifecycleAware by delegate { @Synonym(SynonymType.GROUP, prefix = "Scenario: ") @Descriptions(Description(DescriptionLocation.VALUE_PARAMETER, 0)) fun Scenario(description: String, body: ScenarioBody.() -> Unit) { delegate.group("Scenario: $description”, defaultCachingMode = CachingMode.SCOPE, preserveExecutionOrder = true) { body(ScenarioBody(this)) } } }

Slide 56

Slide 56 text

@SpekDsl class ScenarioBody(val delegate: GroupBody): LifecycleAware by delegate { fun Given(description: String, body: TestBody.() -> Unit) { delegate.test("Given: $description", body = body) } fun When(description: String, body: TestBody.() -> Unit) { delegate.test("When: $description", body = body) } fun Then(description: String, body: TestBody.() -> Unit) { delegate.test("Then: $description", body = body) } fun And(description: String, body: TestBody.() -> Unit) { delegate.test("And: $description", body = body) } }

Slide 57

Slide 57 text

@SpekDsl class ScenarioBody(val delegate: GroupBody): LifecycleAware by delegate { fun Given(description: String, body: TestBody.() -> Unit) { delegate.test("Given: $description", body = body) } fun When(description: String, body: TestBody.() -> Unit) { delegate.test("When: $description", body = body) } fun Then(description: String, body: TestBody.() -> Unit) { delegate.test("Then: $description", body = body) } fun And(description: String, body: TestBody.() -> Unit) { delegate.test("And: $description", body = body) } }

Slide 58

Slide 58 text

@SpekDsl class ScenarioBody(val delegate: GroupBody): LifecycleAware by delegate { fun Given(description: String, body: TestBody.() -> Unit) { delegate.test("Given: $description", body = body) } fun When(description: String, body: TestBody.() -> Unit) { delegate.test("When: $description", body = body) } fun Then(description: String, body: TestBody.() -> Unit) { delegate.test("Then: $description", body = body) } fun And(description: String, body: TestBody.() -> Unit) { delegate.test("And: $description", body = body) } } શ෦ಉ͡ʁ

Slide 59

Slide 59 text

class TodoFeature : Spek({ Feature("Todo") { val now = System.currentTimeMillis() lateinit var todo: Todo Scenario("complete status") { Then(“the todo is not complete") { todo = Todo(id = 1, label = "Write Unit Test with Spek", complete = false, deadline = now) } Then(“complete the todo") { todo.checkComplete() } Then(“the todo is complete”) { assertTrue(todo.complete) } } } }) ͭ·Γ ͜ΕͰ΋0,

Slide 60

Slide 60 text

class TodoFeature : Spek({ Feature("Todo") { val now = System.currentTimeMillis() lateinit var todo: Todo Scenario("changing todo finish status") { Then("todo is not finished ") { todo = Todo(id = 1, label = "Write Unit Test with Spek", finished = false, deadline = now) } Then("mark finish") { todo.markFinished() } Then("it should be true") { assertTrue(todo.finished) } } } }) ॱ൪͸ อূ͞Ε͍ͯΔ @SpekDsl class FeatureBody(val delegate: GroupBody): LifecycleAware by delegate { @Synonym(SynonymType.GROUP, prefix = "Scenario: ") @Descriptions(Description(DescriptionLocation.VALUE_PARAMETER, 0)) fun Scenario(description: String, body: ScenarioBody.() -> Unit) { delegate.group("Scenario: $description”, defaultCachingMode = CachingMode.SCOPE, preserveExecutionOrder = true) { body(ScenarioBody(this)) } } }

Slide 61

Slide 61 text

$PSF$PODFQU https://spekframework.org/core-concepts/

Slide 62

Slide 62 text

Ԡ༻ฤ4QFLͰ -JWF%BUB΍3PPNͷ 6OJU5FTUΛॻ͍ͯΈΑ͏

Slide 63

Slide 63 text

"OESPJEͷน

Slide 64

Slide 64 text

-JWF%BUB+6OJU -JWF%BUBTFU7BMVF -JWF%BUBQPTU7BMVF ʢ%FGBVMU5BTL&YFDVUFSʣ

Slide 65

Slide 65 text

-JWF%BUB+6OJU -JWF%BUBTFU7BMVF -JWF%BUBQPTU7BMVF ʢ%FGBVMU5BTL&YFDVUFSʣ KBWBMBOH3VOUJNF&YDFQUJPO.FUIPE HFU.BJO-PPQFSJOBOESPJEPT-PPQFSOPUNPDLFE PS /VMM1PJOUFS&YDFQUJPO KBWBMBOH3VOUJNF&YDFQUJPO.FUIPE HFU.BJO-PPQFSJOBOESPJEPT-PPQFSOPUNPDLFE PS /VMM1PJOUFS&YDFQUJPO

Slide 66

Slide 66 text

-JWF%BUB+6OJU class InstantTaskExecutorRule : TestWatcher() { protected fun starting(description: Description) { super.starting(description) ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() { override fun executeOnDiskIO(runnable: Runnable) { runnable.run() } override fun postToMainThread(runnable: Runnable) { runnable.run() } override fun isMainThread(): Boolean { return true } }) } protected fun finished(description: Description) { super.finished(description) ArchTaskExecutor.getInstance().setDelegate(null) } }

Slide 67

Slide 67 text

-JWF%BUB+6OJU class InstantTaskExecutorRule : TestWatcher() { protected fun starting(description: Description) { super.starting(description) ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() { override fun executeOnDiskIO(runnable: Runnable) { runnable.run() } override fun postToMainThread(runnable: Runnable) { runnable.run() } override fun isMainThread(): Boolean { return true } }) } protected fun finished(description: Description) { super.finished(description) ArchTaskExecutor.getInstance().setDelegate(null) } } UFTU*NQMFNFOUBUJPOlBOESPJEBSDIDPSFDPSFUFTUJOH7&34*0/

Slide 68

Slide 68 text

-JWF%BUB+6OJU @Rule @JvmField val instantTaskExecutorRule = InstantTaskExecutorRule() ˏ3VMFͰࢦఆ

Slide 69

Slide 69 text

-JWF%BUB+6OJU fun GroupBody.useLiveData() { androidx.arch.core.executor.ArchTaskExecutor.getInstance().setDelega te(object : TaskExecutor() { override fun executeOnDiskIO(runnable: Runnable) { runnable.run() } override fun isMainThread(): Boolean { return true } override fun postToMainThread(runnable: Runnable) { runnable.run() } }) } 4QFL&YUFOUJPOLUΛ ࡞੒

Slide 70

Slide 70 text

-JWF%BUB4QFL class TodoFeature : Spek({ useLiveData() Feature("Todo") { } }) 3PPUͷϥϜμ಺Ͱ ݺͼग़͠

Slide 71

Slide 71 text

-JWF%BUB4QFL class TodoListViewModelSpec : Spek({ useLiveData() val application: Application by lazy { Mockito.mock(Application::class.java) } val todoListViewModel: TodoListViewModel by lazy { TodoListViewModel( application, Schedulers.trampoline() ) } val observer = mock>() lateinit var filterWord: String Feature("TodoViewModel") { Scenario("TodoͷҰཡϑΟϧλʔ͢ΔͱLiveDataʹ৽͍͠஋͕ೖΔ") { Given("LiveData͕ΞΫςΟϒʹͳΔ") { todoListViewModel.wordLiveData.observeForever(observer) } When("ViewModelͷϑΟϧλʔϝιουΛ࣮ߦ͢Δ") { filterWord = "Spek" todoListViewModel.filter(filterWord) } Then("LiveDataͷvalue͕SpekʹͳΔ") { Mockito.verify(observer).onChanged(filterWord) } } } }) 7JFX.PEFMͷ࣋ͭ -JWF%BUBͷ஋͕ มߋ͞Ε͔ͨΛݟΔςετ

Slide 72

Slide 72 text

-JWF%BUB4QFL val observer = mock>() lateinit var filterWord: String Feature("TodoViewModel") { Scenario("TodoͷҰཡϑΟϧλʔ͢ΔͱLiveDataʹ৽͍͠஋͕ೖΔ") { Given("LiveData͕ΞΫςΟϒʹͳΔ") { todoListViewModel.wordLiveData.observeForever(observer) } When("ViewModelͷϑΟϧλʔϝιουΛ࣮ߦ͢Δ") { filterWord = "Spek" todoListViewModel.filter(filterWord) } Then("LiveDataͷvalue͕SpekʹͳΔ") { Mockito.verify(observer).onChanged(filterWord) } } } 0CTFSWFSͷ ϞοΫ࡞੒

Slide 73

Slide 73 text

-JWF%BUB4QFL val observer = mock>() lateinit var filterWord: String Feature("TodoViewModel") { Scenario("TodoͷҰཡϑΟϧλʔ͢ΔͱLiveDataʹ৽͍͠஋͕ೖΔ") { Given("LiveData͕ΞΫςΟϒʹͳΔ") { todoListViewModel.wordLiveData.observeForever(observer) } When("ViewModelͷϑΟϧλʔϝιουΛ࣮ߦ͢Δ") { filterWord = "Spek" todoListViewModel.filter(filterWord) } Then("LiveDataͷvalue͕SpekʹͳΔ") { Mockito.verify(observer).onChanged(filterWord) } } } 0CTFSWFΛ ։࢝͢Δ

Slide 74

Slide 74 text

-JWF%BUB4QFL val observer = mock>() lateinit var filterWord: String Feature("TodoViewModel") { Scenario("TodoͷҰཡϑΟϧλʔ͢ΔͱLiveDataʹ৽͍͠஋͕ೖΔ") { Given("LiveData͕ΞΫςΟϒʹͳΔ") { todoListViewModel.wordLiveData.observeForever(observer) } When("ViewModelͷϑΟϧλʔϝιουΛ࣮ߦ͢Δ") { filterWord = "Spek" todoListViewModel.filter(filterWord) } Then("LiveDataͷvalue͕SpekʹͳΔ") { Mockito.verify(observer).onChanged(filterWord) } } } 0CTFSWFSͷ PO$IBOHFE͕ ݺ͹Ε͔ͨνΣοΫ

Slide 75

Slide 75 text

3PPN3PCPMFDUSJD @RunWith(RobolectricTestRunner::class) @Config(sdk = [O_MR1]) class TodoDataBaseTest { private lateinit var database: TodoDatabase @Before fun openDatabase() { database = Room .inMemoryDatabaseBuilder( InstrumentationRegistry.getInstrumentation().targetContext, TodoDatabase::class.java ).allowMainThreadQueries() .build() } @After fun closeDatabase() { database.close() } @Test @Throws(Exception::class) fun insert() { assertThat(database.todoDao().count(), `is`(0)) val list = listOf( TodoEntity( name = "Robolectric࢖͏", category = 1 ), TodoEntity( name = "JUnit࢖͏", category = 1 ) ) database.todoDao().upsertAll(list) assertThat(database.todoDao().count(), `is`(2)) } }

Slide 76

Slide 76 text

3PPN3PCPMFDUSJD @Before fun openDatabase() { database = Room .inMemoryDatabaseBuilder( InstrumentationRegistry .getInstrumentation() .targetContext, TodoDatabase::class.java ).allowMainThreadQueries() .build() }

Slide 77

Slide 77 text

3PPN3PCPMFDUSJD @Before fun openDatabase() { database = Room .inMemoryDatabaseBuilder( InstrumentationRegistry .getInstrumentation() .targetContext, TodoDatabase::class.java ).allowMainThreadQueries() .build() } ίϯςΩετ͕ ͔ͭ͑Δ

Slide 78

Slide 78 text

3PPN3PCPMFDUSJD @Before fun openDatabase() { database = Room .inMemoryDatabaseBuilder( InstrumentationRegistry .getInstrumentation() .targetContext, TodoDatabase::class.java ).allowMainThreadQueries() .build() }

Slide 79

Slide 79 text

3PPN4QFL 5FTUPOZPVSIPTUNBDIJOF 3PPNVTFTUIF42-JUF4VQQPSU-JCSBSZ XIJDIQSPWJEFT JOUFSGBDFTUIBUNBUDIUIPTFJOUIF"OESPJE'SBNFXPSLDMBTTFT 5IJTTVQQPSUBMMPXTZPVUPQBTTDVTUPNJNQMFNFOUBUJPOTPG UIFTVQQPSUMJCSBSZUPUFTUZPVSEBUBCBTFRVFSJFT

Slide 80

Slide 80 text

3PPN4QFL 5FTUPOZPVSIPTUNBDIJOF 3PPNVTFTUIF42-JUF4VQQPSU-JCSBSZ XIJDIQSPWJEFT JOUFSGBDFTUIBUNBUDIUIPTFJOUIF"OESPJE'SBNFXPSLDMBTTFT 5IJTTVQQPSUBMMPXTZPVUPQBTTDVTUPNJNQMFNFOUBUJPOTPG UIFTVQQPSUMJCSBSZUPUFTUZPVSEBUBCBTFRVFSJFT ˒/PUF&WFOUIPVHIUIJTTFUVQBMMPXTZPVSUFTUTUPSVOWFSZ RVJDLMZ JUJTOUSFDPNNFOEFECFDBVTFUIFWFSTJPOPG42-JUF SVOOJOHPOZPVSEFWJDFŠBOEZPVSVTFSTEFWJDFTŠNJHIUOPU NBUDIUIFWFSTJPOPOZPVSIPTUNBDIJOF

Slide 81

Slide 81 text

3PPN4QFL class TodoDaoSpec : Spek({ useLiveData() val context: Context by lazy { Mockito.mock(Context::class.java) } val todoDatabase: TodoDatabase by lazy { Room.inMemoryDatabaseBuilder( context, TodoDatabase::class.java ).allowMainThreadQueries() .openHelperFactory(JdbcSQLiteOpenHelperFactory()) .build() } Feature("TodoDao") { lateinit var result: Single Scenario("TodoDatabase") { Given("two todos have already been registered") { val todoList = listOf( TodoEntity(name = "SpekͰUnitTestΛॻ͘", finished = false, deadline = 1L), TodoEntity(name = "JUnitͰUnitTestΛॻ͘", finished = false, deadline = 1L) ) todoDatabase.todoDao().upsertAll(todoList) } When("count todo") { result = todoDatabase.todoDao().count() } Then("it should be 2") { result.test().await().assertValue(2) } } } })

Slide 82

Slide 82 text

3PPN4QFL useLiveData() val context: Context by lazy { Mockito.mock(Context::class.java) } val todoDatabase: TodoDatabase by lazy { Room.inMemoryDatabaseBuilder( context, TodoDatabase::class.java ).allowMainThreadQueries() .openHelperFactory(JdbcSQLiteOpenHelperFactory()) .build() }

Slide 83

Slide 83 text

3PPN4QFL useLiveData() val context: Context by lazy { Mockito.mock(Context::class.java) } val todoDatabase: TodoDatabase by lazy { Room.inMemoryDatabaseBuilder( context, TodoDatabase::class.java ).allowMainThreadQueries() .openHelperFactory(JdbcSQLiteOpenHelperFactory()) .build() } +%#$ͷϔϧύʔ Λࢦఆ͢Δ

Slide 84

Slide 84 text

3PPN4QFL useLiveData() val context: Context by lazy { Mockito.mock(Context::class.java) } val todoDatabase: TodoDatabase by lazy { Room.inMemoryDatabaseBuilder( context, TodoDatabase::class.java ).allowMainThreadQueries() .openHelperFactory(JdbcSQLiteOpenHelperFactory()) .build() } ϝΠϯεϨουͷ ֬ೝΛऔΔ

Slide 85

Slide 85 text

3PPN4QFL useLiveData() val context: Context by lazy { Mockito.mock(Context::class.java) } val todoDatabase: TodoDatabase by lazy { Room.inMemoryDatabaseBuilder( context, TodoDatabase::class.java ).allowMainThreadQueries() .openHelperFactory(JdbcSQLiteOpenHelperFactory()) .build() }

Slide 86

Slide 86 text

3PPN4QFL useLiveData() val context: Context by lazy { Mockito.mock(Context::class.java) } val todoDatabase: TodoDatabase by lazy { Room.inMemoryDatabaseBuilder( context, TodoDatabase::class.java ).allowMainThreadQueries() .openHelperFactory(JdbcSQLiteOpenHelperFactory()) .build() } ίϯςΩετ͸ ୅༻Λ༻ҙ

Slide 87

Slide 87 text

3PPN4QFL Scenario("TodoDatabase") { lateinit var result: Single Given("two todos have already been registered") { val todoList = listOf( TodoEntity(name = "SpekͰUnitTestΛॻ͘", complete = false, deadline = 1L), TodoEntity(name = "JUnitͰUnitTestΛॻ͘", complete = false, deadline = 1L) ) todoDatabase.todoDao().upsertAll(todoList) } When("count todo") { result = todoDatabase.todoDao().count() } Then("it should be 2") { result.test().await().assertValue(2) } } ͍͖ͳΓͷ3Y

Slide 88

Slide 88 text

·ͱΊ ɾ4QFL͸+FU#SBJOTࣾ੡ͷςετϑϨʔϜϫʔΫ ɾ+6OJUʹൺ΂ͯه๏͕ಠಛ͕ͩɺΑΓࢿ࢈ʹͳΔςετ͕ॻ͚Δ ɾ࣮ߦॱংΛؾʹ্͔ͤͣΒॻ͜͏ ɾ3PPN΍-JWF%BUBΛ࢖͍ͬͯͯ΋"OESPJEϑϨʔϜϫʔΫ෦෼Λ޻ ෉͢Δͱ6OJU5FTU͸ॻ͚Δ

Slide 89

Slide 89 text

͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ