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
Espressing yourself
Search
Rui Gonçalo
May 30, 2017
Programming
73
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Espressing yourself
Some hints on how to use dagger and mockito to easily build espresso tests.
Rui Gonçalo
May 30, 2017
More Decks by Rui Gonçalo
See All by Rui Gonçalo
NYC Android meetup May 2019
ruigoncalo
0
36
Start speking it @ Droidcon Krakow 2017
ruigoncalo
0
120
Master your custom views
ruigoncalo
1
150
Other Decks in Programming
See All in Programming
OS アップデート対応の取り組み方がもっと共有されてほしい
andpad
0
110
Performance Engineering for Everyone
elenatanasoiu
0
250
吝嗇家のためのAI活用 / AI development for miser - ChatGPT + Issue Driven Development
tooppoo
0
130
The NotImplementedError Problem in Ruby
koic
1
1k
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.7k
正しくソフトウェアを作る、前提を疑うための認知の視点 / doubt-premise
minodriven
21
7.2k
Semantic Version 単位で戦略を柔軟に変えて、パッケージアップデートを自動化する
daitasu
1
330
TypeScript+Orvalで実現する型安全かつ堅牢でスケーラブルなマルチチャネル通知基盤 / TSKaigi Night talks ~after conference~
d0riven
0
380
Creating Composable Callables in Contemporary C++
rollbear
0
180
セキュリティの専門家じゃなくてもできる。「セキュリティ意識」をアップデートして サプライチェーン攻撃への耐性を高めよう。
tk3fftk
5
1k
軽量Java基盤の設計 DIコンテナに頼らない、長期保守と1秒起動の実現 JJUG CCC 2026 Spring
macha64
0
620
エージェンティックRAGにAWSで入門しよう!
har1101
9
1.9k
Featured
See All Featured
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
300
We Are The Robots
honzajavorek
0
260
Code Reviewing Like a Champion
maltzj
528
40k
The browser strikes back
jonoalderson
0
1.3k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
10k
Navigating Weather and Climate Data
rabernat
0
260
Design in an AI World
tapps
1
250
Google's AI Overviews - The New Search
badams
0
1.1k
We Have a Design System, Now What?
morganepeng
55
8.2k
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
190
The SEO Collaboration Effect
kristinabergwall1
1
500
Transcript
espressing yourself @rmgoncalo Rui Gonçalo
Use Espresso to write concise, beautiful, and reliable Android UI
tests
Use Espresso to write concise, beautiful, and reliable Android UI
tests
Use Espresso to write concise, beautiful, and reliable Android UI
tests
MainActivity.kt class MainActivity : AppCompatActivity()
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } } MainActivity.kt
class MainActivity : AppCompatActivity() { @Inject lateinit var presenter: MainPresenter
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } } MainActivity.kt Dagger
MainPresenter.kt class MainPresenter @Inject constructor( private val servicesManager: ServicesManager, private
val getDataUseCase: GetDataUseCase) { private lateinit var view: MainPresenter.View fun attachView(view: MainPresenter.View) { this.view = view } ... interface View { ... } } Dagger
AppComponent.kt @Singleton @Component(modules = arrayOf(AppModule::class)) interface AppComponent { fun plus(module:
ActivityModule): ActivityComponent fun servicesManager(): ServicesManager } Dagger
AppComponent.kt @Singleton @Component(modules = arrayOf(AppModule::class)) interface AppComponent { fun plus(module:
ActivityModule): ActivityComponent fun servicesManager(): ServicesManager } Dagger AppComp AppModule
ActivityComponent.kt @Subcomponent(modules = arrayOf(ActivityModule::class)) interface ActivityComponent { fun plus(module: MainModule):
MainComponent } Dagger AppComp AppModule ActivityComp ActivityModule
MainComponent.kt @Subcomponent(modules = arrayOf(MainModule::class)) interface MainComponent { fun inject(activity: MainActivity)
} Dagger AppComp AppModule ActivityComp ActivityModule MainComp MainModule
MainModule.kt @Module open class MainModule { open fun provideGetDataUseCase() :
GetDataUseCase { return GetDataUseCase } Dagger AppComp AppModule ActivityComp ActivityModule MainComp MainModule
Dagger AppComp AppModule ActivityComp ActivityModule MainComp MainModule class MainActivity :
AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) (application as App) .appComponent .plus(ActivityModule(this)) .plus(MainModule()) .inject(this) } } MainActivity.kt
MainPresenter.kt class MainPresenter @Inject constructor( private val servicesManager: ServicesManager, private
val getDataUseCase: GetDataUseCase) { fun attachView(view: MainPresenter.View) { this.view = view } fun start() { } fun stop() { } }
MainPresenter.kt class MainPresenter (getDataUseCase) { private val subscriptions by lazy
{ CompositeSubscription() } fun start() { subscriptions.add( getDataUseCase.build() .subscribeOn(...) .observeOn(AndroidSchedulers.mainThread()) .subscribe( {result -> view.showResult(result)} , {error -> view.showError(error)}) ) } fun stop() { subscriptions.clear() } }
MainActivity.kt class MainActivity : AppCompatActivity(), MainPresenter.View { @Inject lateinit var
presenter: MainPresenter override fun onStart(){ super.onStart() presenter.start() } override fun onStop(){ super.onStop() presenter.stop() } override fun showResult(result) override fun showError(error) }
MainActivity.kt class MainActivity : AppCompatActivity(), MainPresenter.View { override fun showResult(result)
{ // update layout } override fun showError(error) // update layout }
MainActivityTest.kt @RunWith(AndroidJUnit4::class) @LargeTest class MainActivityTest { @Rule @JvmField val mainActivityTestRule
= ActivityTestRule<MainActivity>(MainActivity::class.java) @Test fun something() { // test } }
AppComp AppModule ActivityComp ActivityModule MainComp MainModule /src/main/java/com/example/ /src/androidTest/java/com/example/ AppComp AppModuleMock
ActivityComp ActivityModuleMock MainComp MainModuleMock
MainActivityTest.kt @RunWith(AndroidJUnit4::class) @LargeTest class MainActivityTest { @Rule @JvmField val mainActivityTestRule
= ActivityTestRule<MainActivity>(MainActivity::class.java) @Test fun something() { // test } } https://github.com/fabioCollini/DaggerMock
MainActivityTest.kt @RunWith(AndroidJUnit4::class) @LargeTest class MainActivityTest { val app = InstrumentationRegistry.getInstrumentation()
.targetContext .applicationContext as App @Rule @JvmField val appComponentRule = DaggerMockRule(AppComponent::class.java, AppModule(app)). set { component -> app.appComponent = component } @Mock lateinit var getDataUseCase: GetDataUseCase @Mock lateinit var servicesManager: ServicesManager } https://github.com/fabioCollini/DaggerMock
MainActivityTest.kt @RunWith(AndroidJUnit4::class) @LargeTest class MainActivityTest { @Mock lateinit var getDataUseCase:
GetDataUseCase @Test fun something() { `when`(getDataUseCase.build()).thenReturn(Observable.just(result)) // test } } https://github.com/fabioCollini/DaggerMock
MainActivityTest.kt @RunWith(AndroidJUnit4::class) @LargeTest class MainActivityTest { @Rule @JvmField val mainActivityTestRule
= ActivityTestRule<MainActivity>(MainActivity::class.java, false, false) @Test fun something() { `when`(getDataUseCase.build()).thenReturn(Observable.just(result)) mainActivityTestRule.launchActivity() // test } } launchActivity
onView(Matcher).perform(ViewAction) onView(Matcher).check(ViewAssertion)
MainActivityTest.kt @RunWith(AndroidJUnit4::class) @LargeTest class MainActivityTest { @Test fun something() {
`when`(getDataUseCase.build()).thenReturn(Observable.just(result)) mainActivityTestRule.launchActivity() onView(withId(R.id.text)).check(matches(withText("result"))) } }
MainActivityTest.kt @RunWith(AndroidJUnit4::class) @LargeTest class MainActivityTest { @Test fun something() {
`when`(getDataUseCase.build()).thenReturn(Observable.just(result)) mainActivityTestRule.launchActivity() onView(withId(R.id.text)).check(matches(withText("result"))) } } http://jakewharton.com/testing-robots
MainActivityTest.kt @RunWith(AndroidJUnit4::class) @LargeTest class MainActivityTest { val mainActions = MainActivityActions()
val mainAssertions = MainActivityAssertions() @Test fun something() { `when`(getDataUseCase.build()).thenReturn(Observable.just(result)) mainActivityTestRule.launchActivity() mainAssertions.isDisplayingResult() } } http://jakewharton.com/testing-robots
Hole19 Android Wear 2.0 app
Hole19 Android Wear 2.0 app
RoundActions.kt class RoundActions { fun onRound(func: RoundActions.() -> Unit) =
RoundActions().apply { func() } fun openDrawer() { onView(withId(R.id.peekView)).perform(click()) } fun selectDistanceUnit(unit: DistanceUnit) { if (unit == DistanceUnit.METRIC) { onView(withId(R.id.metric)).perform(click()) } else { onView(withId(R.id.imperial)).perform(click()) } }
RoundAssertions.kt class RoundAssertions { fun isDrawerOpen() { onView(withId(R.id.drawerContent)).check(matches(isCompletelyDisplayed())) } fun
isDisplayingMetricLabel() { onView(allOf(withId(R.id.distanceUnitLabel), isCompletelyDisplayed())) .check(matches(withText("m"))) } }
RoundActivityTest.kt class RoundActivityTest { @Test fun changeDistanceUnitsToMetric() { `when`(getDistanceUnitsUseCase.build()) .thenReturn(Observable.just(DistanceUnit.IMPERIAL))
roundActivityTestRule.launchActivity() roundActions.onRound { openDrawer() openDistanceUnits() selectDistanceUnit(DistanceUnit.METRIC) } roundAssertions.isDisplayingMetricLabel() }
None
Automatic UI testing spares manual QA tasks Make sure *nothing*
breaks on refactoring Test on a set of emulators/real devices Test on different locales
Thank you @rmgoncalo