Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Espresso 〜コーヒー片手にUIテスト〜
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Taro Nagasawa
December 13, 2013
Technology
3.5k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Espresso 〜コーヒー片手にUIテスト〜
Android Test Casual Talks #1の資料
Taro Nagasawa
December 13, 2013
More Decks by Taro Nagasawa
See All by Taro Nagasawa
Android開発者のための Kotlin Multiplatform入門
ntaro
0
1.8k
Kotlin 最新動向2022 #tfcon #techfeed
ntaro
1
2.4k
#Ubie 狂気の認知施策と選考設計
ntaro
14
14k
UbieにおけるサーバサイドKotlin活用事例
ntaro
1
1.2k
KotlinでSpring 完全理解ガイド #jsug
ntaro
6
3.7k
Kotlinでサーバサイドを始めよう!
ntaro
1
1.1k
Androidからサーバーサイドまで!プログラミング言語 Kotlinの魅力 #devboost
ntaro
5
3.1k
Kotlin Contracts #m3kt
ntaro
4
4.4k
How_to_Test_Server-side_Kotlin.pdf
ntaro
1
590
Other Decks in Technology
See All in Technology
2026-09-08 そのJavaモダナイゼーション、AIに丸投げで大丈夫?IBM Bobで変わる品質と効率
yutanonaka
1
140
Issue 駆動でスペシャリストの意図を届ける、AI 実装のアクセシビリティ向上
thkt
0
130
Railsのように考える: See through the Master
snoozer05
PRO
5
1.3k
Antigravity SDK for the Java Developer
glaforge
0
180
AIは推し活である。
kurazuuuuuu
1
950
AIによるクリエイティブ生成を行う上での試行錯誤
plaidtech
PRO
0
170
SREへの勘違いに気づいた後の話
tomodakengo
0
150
Why Agent Cost Needs Observability
nttcom
0
140
Oracle Cloud Network Path Analyzerを試してみた/I Tried Out Oracle Cloud Network Path Analyzer
masakiokuda
1
110
LLMに渡さなかった仕事
nanaism
0
1.4k
アプリログインとWeb認証基盤をつなぐ ASWebAuthenticationSession 作法
shimastripe
1
390
今話題のAI「Jev」って何? 宇宙最速で学ぶ会
minorun365
PRO
30
19k
Featured
See All Featured
How to train your dragon (web standard)
notwaldorf
97
6.8k
4 Signs Your Business is Dying
shpigford
187
23k
Marketing to machines
jonoalderson
1
5.8k
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
310
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
1k
Rails Girls Zürich Keynote
gr2m
96
14k
jQuery: Nuts, Bolts and Bling
dougneiner
66
8.6k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6.2k
Product Roadmaps are Hard
iamctodd
55
13k
How to Talk to Developers About Accessibility
jct
2
550
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
18k
Music & Morning Musume
bryan
48
7.4k
Transcript
Espresso ʙίʔώʔยखʹUIςετʙ Android Test Casual Talks #1 2013-12-13 ! ຊAndroidͷձ
ொాࢧ෦ @ngsw_taro
ࣗݾհ • ͨΖ͏ @ngsw_taro • ຊAndroidͷձ ொాࢧ෦ • ʮເͱຐ๏ͷͪ࣌ؒʯ։ൃऀ •
ࣗশKotlinΤόϯδΣϦετ
ເͱຐ๏ͷͪ࣌ؒ • σxζχʔϥϯυͷͪ࣌ؒΛௐΔΞϓϦ • 18ສDL • ධՁ 4.4 / 5.0
Kotlin • JetBrainsൃͷJVMݴޠ • Android StudioͰ͑Δʂ • kotlinAndroidLib var clicked
= false! button.setOnClickListener {! ! clicked = true! }
ຊ EspressoͬͯΈͨ
Espressoͱ •UIςετͷҝͷAPIηοτ •Ϣʔβࢹͷςετ͚
UIςετͷҝͷAPIηοτ • ViewMatcher: ViewΛरͬͯ • ViewAction: ViewΛૢ࡞ͯ͠ • ViewAssertion: Viewͷঢ়ଶΛ͔֬ΊΔ
Ϣʔβࢹͷςετ͚ • ʹݟ͑Δ෦Λςετ͢Δ • ϥΠϑαΠΫϧʁϞοΫʁɹɹɹ ͦΜͳΜΒΜ
؆୯ͳྫ
• ໊લΛೖྗ͢ΔͱϘλϯ͕༗ޮʹͳΔ • ϘλϯΛԡ͢ͱѫࡰจ͕දࣔ͞ΕΔ(ผActivity)
public void testInitialNameEditText() {! onView(withId(R.id.nameEditText))! .check(matches(isCompletelyDisplayed()))! .check(matches(hasFocus()))! .check(matches(withText("")));! } nameEditTextͷॳظঢ়ଶͷςετ
public void testInitialNameEditText() {! onView(withId(R.id.nameEditText))! .check(matches(isCompletelyDisplayed()))! .check(matches(hasFocus()))! .check(matches(withText("")));! } ViewInteractionΛฦ͢ɻ
͜Εʹରͯ͠ΞΫγϣϯΛૹͬͨΓɺ Ξαʔτͨ͠Γ͢Δɻ nameEditTextͷॳظঢ়ଶͷςετ
public void testInitialNameEditText() {! onView(withId(R.id.nameEditText))! .check(matches(isCompletelyDisplayed()))! .check(matches(hasFocus()))! .check(matches(withText("")));! } ViewMatcherɻ
View༻ʹಛԽ͞ΕͨMatcher͕͋Δɻ ͜͜ͰResourceIDͰViewΛऔಘɻ nameEditTextͷॳظঢ়ଶͷςετ
public void testInitialNameEditText() {! onView(withId(R.id.nameEditText))! .check(matches(isCompletelyDisplayed()))! .check(matches(hasFocus()))! .check(matches(withText("")));! } ViewInteractionͷϝιουɻ
checkͱperformͷ2ͭ͋Δɻ checkͰViewAssertionΛऔΔɻ nameEditTextͷॳظঢ়ଶͷςετ
public void testInitialNameEditText() {! onView(withId(R.id.nameEditText))! .check(matches(isCompletelyDisplayed()))! .check(matches(hasFocus()))! .check(matches(withText("")));! } ViewAssertionɻ
ViewMatcherΛऔͬͯɺ Ϛον͢Δ͔ݕূ͢Δɻ nameEditTextͷॳظঢ়ଶͷςετ
public void testInitialNameEditText() {! onView(withId(R.id.nameEditText))! .check(matches(isCompletelyDisplayed()))! .check(matches(hasFocus()))! .check(matches(withText("")));! } ViewMatcherɻ
View͕ը໘ʹશͯදࣔ͞Ε͍ͯΕ Ϛον͢Δɻ nameEditTextͷॳظঢ়ଶͷςετ
public void testInitialNameEditText() {! onView(withId(R.id.nameEditText))! .check(matches(isCompletelyDisplayed()))! .check(matches(hasFocus()))! .check(matches(withText("")));! } ςΩετ
ϑΥʔΧεΛ࣋ͬͯΔ nameEditTextͷॳظঢ়ଶͷςετ
public void testInitialGreetButton() {! onView(withId(R.id.greetButton))! .check(matches(isCompletelyDisplayed()))! .check(matches(not(isEnabled())))! .check(matches(withText("Greet!")));! } greetButtonͷॳظঢ়ଶͷςετ
public void testInitialGreetButton() {! onView(withId(R.id.greetButton))! .check(matches(isCompletelyDisplayed()))! .check(matches(not(isEnabled())))! .check(matches(withText("Greet!")));! } greetButtonͷॳظঢ়ଶͷςετ
࠷ॳϘλϯ͕ԡͤͳ͍͜ͱΛ֬ೝ
public void testInputNameEditText() {! onView(withId(R.id.nameEditText))! .perform(typeText(“Taro"));! ! onView(withId(R.id.greetButton))! .check(matches(isEnabled()));! }
nameEditTextʹೖྗ͢Δςετ
public void testInputNameEditText() {! onView(withId(R.id.nameEditText))! .perform(typeText(“Taro"));! ! onView(withId(R.id.greetButton))! .check(matches(isEnabled()));! }
nameEditTextʹೖྗ͢Δςετ ςΩετΛೖྗ͢Δ Viewʹରͯ͠ΞΫγϣϯΛૹΔ Ϙλϯ͕༗ޮ͔֬ೝ
onView(withId(R.id.nameEditText))! .perform(typeText("Taro"));! onView(withId(R.id.greetButton))! .perform(click());! ! onView(withId(R.id.greetView))! .check(matches(isCompletelyDisplayed()))! .check(matches(withText("Hello, Taro!"))); greetButtonΛԡ͢ςετ
onView(withId(R.id.nameEditText))! .perform(typeText("Taro"));! onView(withId(R.id.greetButton))! .perform(click());! ! onView(withId(R.id.greetView))! .check(matches(isCompletelyDisplayed()))! .check(matches(withText("Hello, Taro!"))); greetButtonΛԡ͢ςετ
ΫϦοΫʂ
onView(withId(R.id.nameEditText))! .perform(typeText("Taro"));! onView(withId(R.id.greetButton))! .perform(click());! ! onView(withId(R.id.greetView))! .check(matches(isCompletelyDisplayed()))! .check(matches(withText("Hello, Taro!"))); greetButtonΛԡ͢ςετ
৽͘͠ىಈ͢ΔActivityͷ ViewΛ֬ೝ͢Δʂ
֦ு͕؆୯
͖ͬ͞ςετͯ͠ͳ͍ Hintͱͯ͠ʮNameʯ͕ ઃఆ͞Ε͍ͯΔ͔֬ೝ͍ͨ͠
public void testInitialNameEditText() {! onView(withId(R.id.nameEditText))! .check(matches(isCompletelyDisplayed()))! .check(matches(hasFocus()))! .check(matches(withText(“”)))! .check(matches(withHint(“Name”)))! }
API͕ఏڙ͞Ε͍ͯͳ͍ The method withHint(String) is undefined
public static Matcher<View> withHint(final String hint) {! final Matcher<String> stringMatcher
= is(hint);! checkNotNull(stringMatcher);! return new BoundedMatcher<View, EditText>(EditText.class) {! @Override! public void describeTo(Description description) {! description.appendText("with hint: ");! stringMatcher.describeTo(description);! }! ! @Override! protected boolean matchesSafely(EditText editText) {! return stringMatcher.matches(editText.getHint().toString());! }! };! } ͳ͍ͳΒࣗͰ࡞Εʢry
public static Matcher<View> withHint(final String hint) {! final Matcher<String> stringMatcher
= is(hint);! checkNotNull(stringMatcher);! return new BoundedMatcher<View, EditText>(EditText.class) {! @Override! public void describeTo(Description description) {! description.appendText("with hint: ");! stringMatcher.describeTo(description);! }! ! @Override! protected boolean matchesSafely(EditText editText) {! return stringMatcher.matches(editText.getHint().toString());! }! };! } ͳ͍ͳΒࣗͰ࡞Εʢry ཁhamcrestͷ MatcherΛఆٛ͢Ε͍͍͚ͩ
ͪΐͬͱ໘ͳͱ͜Ζ
ྫ͑ϑϥάϝϯτ Fragment
TextViewʹಉ͡IDΛ͏ R.id.greetView R.id.greetView
ྫ֎͕ى͜Δ onView(withId(R.id.greetView))! .check(matches(withText(“”))); 'with id: is <2131230720>' matches multiple views
in the hierarchy. Ϗϡʔ֊ͰಉҰID͕ෳ͋Δ͔Β
Ճ݅ͰߜΓࠐΉ onView(allOf(! withId(R.id.greetView),! hasSibling(withText("Preview: ")! ))).check(matches(withText("")));
Ճ݅ͰߜΓࠐΉ onView(allOf(! withId(R.id.greetView),! hasSibling(withText("Preview: ")! ))).check(matches(withText(""))); ෳͷMatcherΛऔΔ “Preview: ”ͱදࣔͯ͠Δ ࢞ຓϏϡʔΛ࣋ͭ
ͪΐͬͱ೦ͳͱ͜Ζ
ςετࣦഊ onView(withId(R.id.greetButton))! .check(matches(withText(“Greet!”))); Expected: with text is “Greet!” Actual: with
text is “Greet”
Espresso͞Μ͕ు͘Ϩϙʔτ com.google.android.apps.common.testing.ui.espresso .base.DefaultFailureHandler $AssertionFailedWithCauseError: 'with text: is "Greet!"' doesn't match
the selected view. Expected: with text: is "Greet!" Got: "Button{id=2131230722, res-name=greetButton, visibility=VISIBLE, width=275, height=145, has- focus=false, has-focusable=true, has-window- focus=true, is-clickable=true, is-enabled=false, is- focused=false, is-focusable=true, is-layout- requested=false, is-selected=false, root-is-layout- requested=false, has-input-connection=false, x=142.0, y=240.0, text=Greet, input-type=0, ime- target=false}"
Espresso͞Μ͕ు͘Ϩϙʔτ com.google.android.apps.common.testing.ui.espresso .base.DefaultFailureHandler $AssertionFailedWithCauseError: 'with text: is "Greet!"' doesn't match
the selected view. Expected: with text: is "Greet!" Got: "Button{id=2131230722, res-name=greetButton, visibility=VISIBLE, width=275, height=145, has- focus=false, has-focusable=true, has-window- focus=true, is-clickable=true, is-enabled=false, is- focused=false, is-focusable=true, is-layout- requested=false, is-selected=false, root-is-layout- requested=false, has-input-connection=false, x=142.0, y=240.0, text=Greet, input-type=0, ime- target=false}"
Espresso͞Μ͕ు͘Ϩϙʔτ com.google.android.apps.common.testing.ui.espresso .base.DefaultFailureHandler $AssertionFailedWithCauseError: 'with text: is "Greet!"' doesn't match
the selected view. Expected: with text: is "Greet!" Got: "Button{id=2131230722, res-name=greetButton, visibility=VISIBLE, width=275, height=145, has- focus=false, has-focusable=true, has-window- focus=true, is-clickable=true, is-enabled=false, is- focused=false, is-focusable=true, is-layout- requested=false, is-selected=false, root-is-layout- requested=false, has-input-connection=false, x=142.0, y=240.0, text=Greet, input-type=0, ime- target=false}"
ʊਓਓਓਓਓਓਓʊ ʼɹಡΈͮΒ͍ɹʻ ʉY^Y^Y^Y^Y^Yʉ
·ͱΊ • EspressoViewʹಛԽͨ͠ςετAPI • hamcrest͔ͩΒҠߦίετ͘ɺ֦ுੑߴ͠ • ͍͔ʹMatcherΛΈ߹ΘͤΔ͔͕ॏཁͬΆ͍ • ϨϙʔτಡΈͮΒ͍ •
KotlinૉΒ͍͠ݴޠ
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ɻ @ngsw_taro ເͱຐ๏ͷͪ࣌ؒ