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
5k
Spring BootをKotlinで作成しAmazon Elastic Container Service (ECS) で稼働させる
srym
6
1.9k
iOSDC_2019_DeviceFarm.pdf
srym
8
19k
世界で戦うエンジニアになるために_公開用.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
600
I/O 2017 Short Report
srym
0
310
Other Decks in Technology
See All in Technology
テストを書かないためのテスト/ Tests for not writing tests
sinsoku
1
170
Oracle Exadata Database Service(Dedicated Infrastructure):サービス概要のご紹介
oracle4engineer
PRO
0
12k
Amazon Q Developerで.NET Frameworkプロジェクトをモダナイズしてみた
kenichirokimura
1
200
comilioとCloudflare、そして未来へと向けて
oliver_diary
6
450
あなたの知らないクラフトビールの世界
miura55
0
130
RubyでKubernetesプログラミング
sat
PRO
4
160
Cloudflareで実現する AIエージェント ワークフロー基盤
kmd09
0
290
タイミーのデータ活用を支えるdbt Cloud導入とこれから
ttccddtoki
1
160
シフトライトなテスト活動を適切に行うことで、無理な開発をせず、過剰にテストせず、顧客をビックリさせないプロダクトを作り上げているお話 #RSGT2025 / Shift Right
nihonbuson
3
2.2k
.NET AspireでAzure Functionsやクラウドリソースを統合する
tsubakimoto_s
0
190
デジタルアイデンティティ技術 認可・ID連携・認証 応用 / 20250114-OIDF-J-EduWG-TechSWG
oidfj
2
680
Goで実践するBFP
hiroyaterui
1
120
Featured
See All Featured
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
The Cost Of JavaScript in 2023
addyosmani
46
7.2k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
27
1.5k
The Invisible Side of Design
smashingmag
299
50k
Fantastic passwords and where to find them - at NoRuKo
philnash
50
2.9k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
33
2k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
28
2.2k
Speed Design
sergeychernyshev
25
740
Raft: Consensus for Rubyists
vanstee
137
6.7k
Code Reviewing Like a Champion
maltzj
521
39k
A better future with KSS
kneath
238
17k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
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ࢯʹҾ͖ܧ͗ ·͢ʂ