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
Android Testing Bootcamp #3 Hands-On
Search
Fumihiko Shiroyama
September 09, 2016
Technology
4
740
Android Testing Bootcamp #3 Hands-On
Android Testing Bootcamp #3 Hands-On 資料
Fumihiko Shiroyama
September 09, 2016
Tweet
Share
More Decks by Fumihiko Shiroyama
See All by Fumihiko Shiroyama
The world of Android wireless communications without Internet
srym
1
130
AWS Device FarmとCircleCIでAndroidのUIテストを自動化しよう
srym
1
5.1k
Spring BootをKotlinで作成しAmazon Elastic Container Service (ECS) で稼働させる
srym
6
2k
iOSDC_2019_DeviceFarm.pdf
srym
8
20k
世界で戦うエンジニアになるために_公開用.pdf
srym
18
45k
Unit Testing in a Nutshell - DroidKaigi 2018
srym
11
15k
Clean Architecture & TDD
srym
1
3.8k
はやい・やすい・うまい!スタートアップでも使える Retrofit + RxJava で瞬間APIクッキングレシピ
srym
2
620
I/O 2017 Short Report
srym
0
310
Other Decks in Technology
See All in Technology
JavaにおけるNull非許容性
skrb
2
2.6k
設計を積み重ねてシステムを刷新する
sansantech
PRO
0
160
データエンジニアリング領域におけるDuckDBのユースケース
chanyou0311
9
2.2k
あなたが人生で成功するための5つの普遍的法則 #jawsug #jawsdays2025 / 20250301 HEROZ
yoshidashingo
2
280
Two Blades, One Journey: Engineering While Managing
ohbarye
4
1.9k
サイト信頼性エンジニアリングとAmazon Web Services / SRE and AWS
ymotongpoo
7
1.5k
クラウド食堂とは?
hiyanger
0
110
【内製開発Summit 2025】イオンスマートテクノロジーの内製化組織の作り方/In-house-development-summit-AST
aeonpeople
2
620
AWSを活用したIoTにおけるセキュリティ対策のご紹介
kwskyk
0
350
Change Managerを活用して本番環境へのセキュアなGUIアクセスを統制する / Control Secure GUI Access to the Production Environment with Change Manager
yuj1osm
0
100
入門 PEAK Threat Hunting @SECCON
odorusatoshi
0
150
偏光画像処理ライブラリを作った話
elerac
1
170
Featured
See All Featured
Unsuck your backbone
ammeep
669
57k
Art, The Web, and Tiny UX
lynnandtonic
298
20k
Thoughts on Productivity
jonyablonski
69
4.5k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.4k
A Philosophy of Restraint
colly
203
16k
Documentation Writing (for coders)
carmenintech
67
4.6k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
27
1.9k
KATA
mclloyd
29
14k
Producing Creativity
orderedlist
PRO
344
40k
The Invisible Side of Design
smashingmag
299
50k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
33
2.1k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
28
9.3k
Transcript
"OESPJE5FTUJOH#PPUDBNQ )BOET0O "OESPJE@5FTUJOH@#$
w "OESPJE5FTUJOH#PPUDBNQ(SPVQ w IUUQBOESPJEUFTUJOH CPPUDBNQDPOOQBTTDPN w "OESPJE5FTUJOH#PPUDBNQ w IUUQBOESPJEUFTUJOH CPPUDBNQDPOOQBTTDPNFWFOU
ࠓճར༻͢Δڥ
w "OESPJE4UVEJP w IUUQTHJUIVCDPNTSZN5FTU)BOET0O w HJUDMPOFHJU!HJUIVCDPNTSZN 5FTU)BOET0OHJU w HJUDPFYBNQMFྫϒϥϯν w
HJUDPBOTXFSղྫϒϥϯν
ΞδΣϯμ
w ͳͥςετΛॻ͘ͷ͔ w ςετͷछྨ w +6OJU w .PDLJUP w 1PXFS.PDL
w %PHHFS 6*ςετ ࣍ճϋϯζΦϯΛ ָ͓͠Έʹʂ
ͳͥςετΛॻ͘ͷ͔
w ϩδοΫͷݕূ w ༷มߋͷै w σάϨͷݕग़ w ઃܭͷݟ͠ͷ͖͔͚ͬ
ςετͷछྨ
w ୯ମςετ 6OJU5FTU w ݁߹ςετ *OUFHSBUJPO5FTU w Ϣʔβड͚ೖΕςετ
6TFS"DDFQUBODF5FTU w ͦͷଞɺෛՙςετɾηΩϡϦςΟςετ
6OJU5FTU w ϓϩάϥϚ͕࣮ w ϝιουΫϥεʹରʹରԠ w ࣗಈ࣮ߦ w ߴ w
"OESPJEͷ߹ɺ࣮ػڥͰͳ͍͜ͱ͕ଟ͍
*OUFHSBUJPO5FTU w 6*ʹର͢ΔࣗಈςετΛؚΉ w ࣮ࡍͷը໘ભҠ*0ॲཧɺωοτϫʔΫ௨৴Λߦ ͏͜ͱଟ͍ w ࣮ػʢΤϛϡϨʔλؚΉʣ࣮ߦ ఆٛӡ༻ ݱʹΑͬͯҧ͏
ड͚ೖΕςετ w ൃݩɺϚωʔδϟɾσΟϨΫλ͕֬ೝ w 2"͞ΜɺςελʹΑΔ֬ೝ w ଟ͘ͷ߹खಈ ఆٛӡ༻ ݱʹΑͬͯҧ͏
ͦͷଞ w ෛՙςετ w ηΩϡϦςΟςετ w ͦͷଞॾʑ
+6OJU
w "OESPJEͷ6OJU5FTUͷσϑΝΫτελϯμʔυ w "OESPJE4UVEJPҎ߱Λ͍ͬͯΕԿ͠ͳ͘ ͯར༻Մೳ w Ξϊςʔγϣϯϕʔε w +7.ڥͷΈͰಈ࡞͠ඇৗʹߴ
CVJMEHSBEMF EFQFOEFODJFT\ DPNQJMFpMF5SFF EJSMJCT JODMVEF< KBS> UFTU$PNQJMFKVOJUKVOJU DPNQJMFDPNBOESPJETVQQPSUBQQDPNQBUW ^
+6OJUͷجຊ
w NPEVMFTSDUFTUKBWBQBDLBHF5BSHFU5FTUKBWB w !#FGPSFʜॳظԽ w !"GUFSʜޙॲཧ w !5FTUʜςετίʔυຊମ
public class CalculatorTest { private Calculator calculator; @Before public
void setUp() { calculator = new Calculator(); } @After public void tearDown() { } @Test public void ͠ࢉ͕ਖ਼͘͠ग़དྷΔ() throws Exception { double expected = calculator.add(1.0, 2.0); assertTrue(expected == 3.0); } }
"TTFSUJPO w "TTFSUBTTFSU5SVF CPPMFBODPOEJUJPO w "TTFSUBTTFSU&RVBMT 0CKFDUFYQFDUFE 0CKFDU BDUVBM
՝ w )FMMP+6OJUΛͤ͞Α͏ʂ w IUUQTHJUIVCDPNTSZN5FTU)BOET0OCMPC FYBNQMFKVOJUTSDUFTUKBWBVTTIJSPZBNBBOESPJE KVOJU)FMMP+6OJUKBWB w
$BMDVMBUPS5FTUΛͤ͞Α͏ʂ w IUUQTHJUIVCDPNTSZN5FTU)BOET0OCMPC FYBNQMFKVOJUTSDUFTUKBWBVTTIJSPZBNBBOESPJE KVOJU$BMDVMBUPS5FTUKBWB
.BUDIFS w .BUDIFSʜʮͲΜͳঢ়ଶ͔ʯΛද͢ w "TTFSUBTTFSU5IBU BDUVBM NBUDIFS w )BNDSFTUͷ.BUDIFST͕༗໊
w IUUQIBNDSFTUPSH+BWB)BNDSFTU
Assert.assertThat(1 + 1, Matchers.is(2)); assertThat("MIKE", is(not("STEVE"))); assertThat(1000L, is(instanceOf(Integer.class)));
EFQFOEFODJFT\ UFTU$PNQJMFKVOJUKVOJU UFTU$PNQJMFPSHIBNDSFTUIBNDSFTUMJCSBSZ ^
Α͘͏.BUDIFS w JT ʜಉ w OPU ʜ൱ఆ w OVMM7BMVF OPU/VMM7BMVF
ʜ/VMMʗඇ/VMM w TBNF*OTUBODF ʜಉҰΠϯελϯε w JOTUBODF0G ʜಉҰ͔αϒΫϥεͷΠϯελϯε w TUBSUT8JUI FOET8JUI ʜࢦఆจࣈྻͰ࢝·ΔʗऴΘΔ w DPOUBJOT4USJOH ʜࢦఆจࣈྻؚ͕·ΕΔ w JT&NQUZ4USJOH ʜۭจࣈྻ
w HSFBUFS5IBO HSFBUFS5IBO0S&RVBM5P ʜΑΓେ͖͍ʗҎ ্ w MFTT5IBO MFTT5IBO0S&RVBM5P ʜΑΓখ͍͞ʗҎԼ w
DMPTF5P ʜࢦఆͷൣғ͔Ͳ͏͔ w BTTFSU5IBU JT DMPTF5P ʙൣ ғͳͷͰUSVF w ͦͷଞɺϦετϚοϓ༻ͷ.BUDIFSଟ w ࢀߟ IUUQRJJUBDPNPQFOHMJUFNT FEBCFGBB
՝ w $BMDVMBUPS5FTU8JUI)BNDSFTU.BUDIFSTΛ ͤ͞Α͏ʂ w IUUQTHJUIVCDPNTSZN5FTU)BOET0OCMPC FYBNQMFKVOJUTSDUFTUKBWBVTTIJSPZBNB BOESPJEKVOJU $BMDVMBUPS5FTU8JUI)BNDSFTU.BUDIFSTKBWB
.PDLJUP
w ϝιουΛ͋Β͔͡ΊܾΊΒΕͨ݁Ռ͕ฦΔΑ͏ʹ ϞοΫΞοϓ͢Δ w *0ɺωοτϫʔΫΞΫηεɺϥϯμϜੑͷഉআ w ͦͷͰςετ͍߲ͨ͠ʹूத͢Δ w .PDLJUP͕σϑΝΫτελϯμʔυ
public class RandomIntGenerator { private final Random random = new
Random(); public int generate() { return random.nextInt(Integer.MAX_VALUE); } }
public class RandomIntGeneratorTest { private RandomIntGenerator randomIntGenerator; @Before public
void setUp() { randomIntGenerator = new RandomIntGenerator(); } @After public void tearDown() { } @Test public void generateͷ݁Ռ() { int expected = randomIntGenerator.generate(); assertThat(expected, is(1)); // ͜Μͳ͜ͱͰ͖ͳ͍ʂʂ } }
͍ํ RandomIntGenerator randomIntGenerator = Mockito.mock(RandomIntGenerator.class); Mockito.when(randomIntGenerator.generate()).thenReturn(123);
w ଞʹ!.PDLΞϊςʔγϣϯΛ͏Γํ͕͋Δ
@Mock RandomIntGenerator randomIntGeneratorWithAnnotation; @Before public void setUp() {
MockitoAnnotations.initMocks(this); Mockito.when(randomIntGeneratorWithAnnotation.generate()).thenReturn(456); }
՝ w 3BOEPN*OU(FOFSBUPS5FTUΛͤ͞Α͏ʂ w IUUQTHJUIVCDPNTSZN5FTU)BOET0OCMPC FYBNQMFNPDLJUPTSDUFTUKBWBVTTIJSPZBNB BOESPJENPDLJUP 3BOEPN*OU(FOFSBUPS5FTUKBWB w
!.PDLΞϊςʔγϣϯΛ͏ํ๏ࢼͯ͠ΈΑ ͏ʂ
.PDLJUPWFSJGZ
w .PDLΦϒδΣΫτɺݺͼग़͠Λͯ͢هԱͯ͠ ͍Δ w ݺͼग़͞ΕͨϝιουɺճɺҾͳͲΛ .PDLJUPWFSJGZͰݕূͰ͖Δ
Mockito.verify(mockStrings, Mockito.times(1)).add("FOO"); Mockito.verify(mockStrings, Mockito.atLeast(1)).add("BAR"); Mockito.verify(mockStrings).add("BAZ"); Mockito.verify(mockStrings, Mockito.never()).add("I AM NOT CALLED");
EFQFOEFODJFT\ UFTU$PNQJMFKVOJUKVOJU UFTU$PNQJMFPSHIBNDSFTUIBNDSFTUMJCSBSZ UFTU$PNQJMFPSHNPDLJUPNPDLJUPDPSF ^
.PDLJUPTQZ
w ΦϒδΣΫτΛͦͷ··ϞοΫΞοϓ͢Δ .PDLJUPNPDLʹର͠ɺTQZ࣮ΦϒδΣΫτͷҰ ෦ͷΈΛࠩ͠ସ͑ΒΕΔ w TQZΦϒδΣΫτNPDLͱಉ༷WFSJGZͰݕূͰ͖Δ w TQZͰΛฦ͢ͱ͖ XIFO NPDL
UIFO3FUVSO WBMVF Ͱͳ͘ EP3FUVSO WBMVF XIFO NPDL NFUIPE Λ͏ w !.PDLಉ༷ɺ!4QZΞϊςʔγϣϯ͕ར༻Մೳ
List<String> realList = new ArrayList<>(); List<String> spyList = spy(realList); doReturn(38).when(spyList).size();
ϑΟʔϧυʹ.PDLΛ ૹΓ͜Ή
public class StringCollector { private List<String> strings = new ArrayList<>();
public void add(@NonNull String string) { strings.add(string); } public int size() { return strings.size(); } }
w ϑΟʔϧυͷ"SSBZ-JTUʹͲ͏ͬͯϞοΫΛૹΓ ࠐΉʁ w TFUUFSΛ༻ҙͯ͠!7JTJCMF'PS5FTUJOH w !*OKFDU.PDLTΞϊςʔγϣϯ
public class StringCollector { private List<String> strings = new ArrayList<>();
public void add(@NonNull String string) { strings.add(string); } public int size() { return strings.size(); } @VisibleForTesting void setStrings(List<String> strings) { this.strings = strings; } }
!*OKFDU.PDLT @Mock List<String> mockStringsForInject; @InjectMocks StringCollector stringCollectorForInject;
՝ w 4USJOH$PMMFDUPS5FTUΛͤ͞Α͏ʂ w IUUQTHJUIVCDPNTSZN5FTU)BOET0OCMPC FYBNQMFNPDLJUPTSDUFTUKBWBVTTIJSPZBNB BOESPJENPDLJUP4USJOH$PMMFDUPS5FTUKBWB w
!4QZΞ ϊςʔγϣϯΛ͏ํ๏ࢼͯ͠ΈΑ͏ʂ w !*OKFDU.PDLTΞϊςʔγϣϯͬͯΈΑ͏ʂ
.PDLJUPࢀߟϦϯΫ w IUUQFUDIBUFOBCMPHDPNFOUSZ w IUUQFUDIBUFOBCMPHDPNFOUSZ w IUUQFUDIBUFOBCMPHDPNFOUSZ
1PXFS.PDL
w ࠓճ͕࣌ؒͳ͍ͷͰ৮Ε·ͤΜ w pOBMDMBTTTUBUJDNFUIPEΛϞοΫͰ͖Δ w Ϋϥεϩʔμʹհೖ͢ΔͷͰϋϚϦϙΠϯτ͕͍ͬ ͺ͍͋Δ w ಟʹༀʹͳΓ͏Δ
ςετ͍͢͠ߏԽ
w DPNNPOϞδϡʔϧͷ6TFS*OGP6TFS$BTFͷղઆ w IUUQTHJUIVCDPNTSZN5FTU)BOET0OCMPC FYBNQMFDPNNPOTSDNBJOKBWBVT TIJSPZBNBBOESPJEDPNNPOVTFDBTF 6TFS*OGP6TF$BTF*NQMKBWB
՝ w 6TFS*OGP6TF$BTF*NQM5FTUΛͤ͞Α͏ʂ w IUUQTHJUIVCDPNTSZN5FTU)BOET0OCMPC FYBNQMFNPDLJUPTSDUFTUKBWBVTTIJSPZBNB BOESPJENPDLJUP 6TFS*OGP6TF$BTF*NQM5FTUKBWB w
ඇಉظॲཧͷςετͷώϯτɿ$PVOU%PXO-BUDI
w ςετΛͳΔ͍͘͢͠ߏʹ͢Δ w దʹΠϯλϑΣʔεΛΔ w ґଘੑΛίϯετϥΫλͰ͢ w %*ίϯςφΛ͏
%BHHFS
w (PPHMF͕ϝϯςφϯε͍ͯ͠Δ%*ίϯςφ w IUUQHPPHMFHJUIVCJPEBHHFS w .PEVMFͱ$PNQPOFOUΛͬͯґଘੑͷάϥϑΛ ఆٛ͠ɺ!*OKFDUΞϊςʔγϣϯͰ࣮Πϯελϯε Λࠩ͠ࠐΉ w ςετ࣌ʹΠϯελϯεΛΓସ͑ΒΕΔͳͲॊೈ
ੑ͕૿͢
@Module public class ApplicationModule { @Singleton @Provides public MessageHelper provideMessageHelper(Context
context) { return MessageHelper.getInstance(context); } }
@Singleton @Component(modules = ApplicationModule.class) public interface ApplicationComponent { ActivityComponent activityComponent(ActivityModule
module); }
public ApplicationComponent getComponent() { return applicationComponent; } @Override public
void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.builder() .applicationModule(new ApplicationModule(this)) .build(); }
public class BaseActivity extends AppCompatActivity { private ActivityComponent activityComponent;
/** * {@link android.app.Activity}ͷґଘੑάϥϑΛฦ͢ * * @return */ protected ActivityComponent getComponent() { if (activityComponent == null) { MyApplication myApplication = (MyApplication) getApplication(); activityComponent = myApplication.getComponent().activityComponent(new ActivityModule(this)); } return activityComponent; } }
public class ChatActivity extends BaseActivity { @Inject MessageHelper messageHelper;
// ҎԼུ }
w %BHHFSΛ࣮ͬͨફతͳྫOVNBࢯʹҾ͖ܧ͗ ·͢ʂ