$30 off During Our Annual Pro Sale. View Details »

古典部 開発合宿発表スライド

古典部 開発合宿発表スライド

今回はAndroidのTestがんばるぞい

Rei Matsushita

August 13, 2016
Tweet

More Decks by Rei Matsushita

Other Decks in Technology

Transcript

  1. ANDROID UI TEST TRAINING
    ݹయ෦ ։ൃ߹॓ VOL.2

    View Slide

  2. ࠓճτϥΠͨ͜͠ͱ
    ▸ લճͷ߹॓Ͱ࡞ͬͨHBFavͷKotlin/Android൛ͷઃܭͷݟ௚
    ͠ͱUIςετΛ࣮૷͢Δ
    ▸ ࠓ·Ͱ͋·ΓςετΛॻ͍ͯདྷΕͳ͔ͬͨͷͰɺࠓճͷ·ͱ
    ·ͬͨ࣌ؒͰςετ͠΍͍͢ઃܭͱςετΛॻ͘஌ݟΛಘΔ

    View Slide

  3. ςΩετ
    ΞϓϦͷઃܭͷ࿩
    ▸ ࢖༻લ
    Model Controller View
    Activity / Fragment XML

    View Slide

  4. ςΩετ
    ΞϓϦͷઃܭͷ࿩
    ▸ ࢖༻ޙ
    Presenter View
    Activity /
    Fragment
    XML
    Infra Model
    API
    Preferences Repository
    Service Entity
    DDDͬΆ͍Something
    ΠϕϯτΛ؅ཧ
    Value Object

    View Slide

  5. ςΩετ
    ͜Μͳײ͡ͷΠϯλʔϑΣʔεͰܨ͛Δ
    interface BookmarkFavoriteContact {


    interface View {


    fun showBookmarkList(bookmarkList: List)


    fun hideBookmarkList()


    fun showNetworkErrorMessage()


    fun showProgress()


    fun hideProgress()


    fun startAutoLoading()


    fun stopAutoLoading()


    fun showEmpty()


    fun hideEmpty()

    }


    interface Actions {


    fun onCreate(component: FragmentComponent,

    view: BookmarkFavoriteContact.View)


    fun onResume()


    fun onPause()


    fun onRefreshList()


    fun onScrollEnd(nextIndex: Int)


    fun onClickBookmark(bookmarkEntity: BookmarkEntity)

    }

    }

    View Slide

  6. ςΩετ
    MVPͳߏ੒ʹ͢Δ͜ͱͰϨΠϠʔͷ੹຿Λ໌֬ʹ͢Δ
    ▸ Activity / Fragment͸Model૚΁ͷΞΫηε͸ߦΘͣɺUIεϨουͰൃੜ
    ͢ΔΠϕϯτ͸Presenter΁ྲྀ͢ɻActivity / Fragment͸Viewͷૢ࡞ͷΈ
    ؔ৺Λ࣋ͭΑ͏ʹ͢Δɻ
    ▸ Presenter͸ड͚औͬͨΠϕϯτʹରԠ͢ΔModelͷૢ࡞Λߦ͏ʴૢ࡞ʹ
    Ԡͨ͡ViewͷίϯτϩʔϧΛߦ͏ɻViewͷ۩ମతͳૢ࡞ʹ͸ؔ৺Λ࣋ͨ
    ͣݺͼग़͚ͩ͢ɻ
    ▸ Model૚͸ن໛ʹΑΔ͚ͲDDDͬΆ͍Ξϓϩʔν͕ϝϯς͠΍͍͔͢ͳ
    ͱɻͨͩɺΞϓϦ͸API࿈ܞ͕΄΅ඞਢͳͷͰAPI͕བྷΉϞσϦϯά͸Ͳ
    ͏͢Ε͹͍͍Μ͡ΌΖͱ͍͏ײ͡ɻ

    View Slide

  7. ςΩετ
    UIςετॻͧ͘
    ▸ MVPʹͯ͠ɺͦΕͧΕͷϨΠϠʔΛந৅Խͨ͜͠ͱͰView
    ͷςετ͕ॻ͖΍͘͢ͳͬͨ
    ▸ ·ͣ͸Contact.ActionsΛ࣮૷ͨ͠MockΛςετ࣌ʹView
    ʹ஫ೖ͢ΔΑ͏ʹͯ͠ɺViewʹର͢ΔςετΛॻ͘ɻ

    View Slide

  8. ςΩετ
    ࢖͏πʔϧ
    ▸ Dagger2 / DI༻ͷϥΠϒϥϦɻΞϊςʔγϣϯɾϓϩηογ
    ϯάͱ͍͏࢓૊ΈΛ࢖ͬͯϏϧυ࣌ʹΞϊςʔγϣϯʹैͬ
    ͯDI༻ͷΫϥεϑΝΠϧΛੜ੒ͯ͘͠ΕΔɻ
    ▸ Espresso / ඪ४ͷύοέʔδʹೖ͍ͬͯΔUIςετπʔϧɻ

    View Slide

  9. ςΩετ
    ͜Μͳײ͡Ͱςετ࣌ʹDI͢ΔϞδϡʔϧΛࠩ͠ସ͑Δ͜ͱ͕Ͱ͖Δ͍ͧ
    @RunWith(AndroidJUnit4::class)

    class SplashActivityTest {
    @Rule

    @JvmField

    val activityRule: ActivityTestRule = object : ActivityTestRule(SplashActivity::class.java) {


    override fun beforeActivityLaunched() {

    super.beforeActivityLaunched()


    val app = InstrumentationRegistry.getTargetContext().applicationContext as App


    val appComponent = DaggerSplashActivityTest_TestApplicationComponent.builder()

    .applicationModule(ApplicationModule(app))

    .infraLayerModule(InfraLayerModule())

    .build()


    app.component = appComponent

    }

    }
    @Singleton

    @Component(modules = arrayOf(ApplicationModule::class, InfraLayerModule::class))

    interface TestApplicationComponent : ApplicationComponent {

    override fun activityComponent(): TestActivityComponent

    }


    @Subcomponent(modules = arrayOf(ActivityModule::class))

    interface TestActivityComponent : ActivityComponent {

    override fun fragmentComponent(): TestFragmentComponent

    }


    @Subcomponent(modules = arrayOf(TestInitializeFragmentModule::class))

    interface TestFragmentComponent : FragmentComponent {

    override fun inject(fragment: InitializeFragment)

    }

    View Slide

  10. ςΩετ
    @Module

    class TestInitializeFragmentModule() : FragmentModule() {


    override fun createInitializePresenter(context: Context): InitializeContact.Actions {

    return object : InitializeContact.Actions {


    override lateinit var view: InitializeContact.View


    override fun onCreate(component: FragmentComponent, view: InitializeContact.View) {

    this.view = view

    }


    override fun onResume() {


    }


    override fun onPause() {


    }


    override fun onClickButtonSetId(userId: String) {

    if (userId == "valid") {

    view.navigateToMain()

    } else if (userId == "invalid") {

    view.displayInvalidUserIdMessage()

    } else {

    view.showNetworkErrorMessage()

    }

    }

    }

    }

    }
    }

    View Slide

  11. ςΩετ
    ςετέʔε͸͜Μͳ;͍Μ͖
    @Test

    fun ॳظදࣔͷ֬ೝ() {


    // ͸ͯͳIDೖྗཝ͕දࣔ.

    onView(withId(R.id.fragment_initialize_edit_hatena_id)).check(matches(isDisplayed()))


    // ͸ͯͳIDૹ৴Ϙλϯ͕ແޮͳঢ়ଶͰදࣔ.

    onView(withId(R.id.fragment_initialize_button_set_hatena_id)).check(matches(isDisplayed()))

    onView(withId(R.id.fragment_initialize_button_set_hatena_id)).check(matches(not(isEnabled())))


    // FAB͸දࣔ͞Εͯͳ͍.

    onView(withId(R.id.fab)).check(matches(not(isDisplayed())))

    }
    @Test

    fun ਖ਼͍͠͸ͯͳID͕ೖྗ͞ΕΔͱϝΠϯը໘ʹߦ͘() {

    onView(withId(R.id.fragment_initialize_edit_hatena_id)).perform(typeText("valid"))

    onView(withId(R.id.fragment_initialize_button_set_hatena_id))

    .perform(closeSoftKeyboard())

    .perform(click())


    // ϝΠϯը໘ͷπʔϧόʔ͕දࣔ͞ΕͨΒભҠͨ͠ͱΈͳ͢.

    onView(withId(R.id.app_bar_main_toolbar)).check(matches(isDisplayed()))

    }

    View Slide

  12. ςΩετ
    ͸·ͬͨͱ͜Ζ
    ▸ ͞ΒͬͱDIͷͩ͘ΓΛॻ͍͚ͨͲςετ༻ͷϞδϡʔϧͷࠩ
    ͠ସ͑ํ๏͕࠷ॳͷํͰ͏·͍͔͘ͳͯ͘͸·ͬͨɻDIͷ
    Ϟδϡʔϧͷؔ࿈෇͚͕ඍົͳ࡞Γʹͳͬͯͨͷʹؾ͔ͮͳ
    ͔ͬͨ
    ▸ ͜ͷεϥΠυΛॻ͍ͯΔ࣌఺ͰCircleCI্ͷϏϧυ͕͏·͘
    ͍ͬͯͳ͍ɻɻɻςετ࣮ߦ͢Δͱ͜ΖͰແ࣊൵ͳλΠϜΞ
    ΢τͰ͚͜ΔɻϩʔΧϧͰ͸໰୊ແ͍ɻ

    View Slide

  13. ςΩετ
    ·ͱΊ
    ▸ Android͸ద౰ʹॻ͘ͱFatͳActivity / FragmentʹͳΓ΍͢
    ͍ͷͰɺ੹຿ͷ෼ׂΛҙࣝͨ͠ઃܭΛ͠·͠ΐ͏ɻ
    ▸ Dagger2࢖ͬͯDI͢Δ͜ͱͰςετ͠΍͍͢࢓૊ΈΛ࡞Ε
    ΔΑɻ
    ▸ https://github.com/rei-m/HBFav_material

    View Slide

  14. ৽͍͠෢ثΛखʹೖΕΔͱ͖Ͳ͏ͯ͠΋Ұ࣌తʹ݁
    Ռ͸ѱ͘ͳΔʢѱ͘ͳͬͯͳ͍Αʂʣ
    ͦΕͰ΋खʹೖΕʹ͍͔͘
    ͢Ͱʹ࢖͑Δ෢ث͚ͩΛΑΓຏ͔͘
    ग़యʮͪ͸΍;Δʯ
    ͓͠·͍
    ͜Ε͔Β΋߈Ίଓ͚Δ

    View Slide