以前主催した社内勉強会 "Testability exercise" の導入として説明した、ユニットの意義とその使い方の概念を掴むための資料です。 サーバーレス(およびServerless Framework)を扱う案件が社内的にも増加しているので、その手の話題も含まれています。
書いてある内容は発表当時の私の見解です。人により異論はあると思うのでフィードバックお待ちしています。
Unittest ͷೖ#testability-exercise
View Slide
※͜ͷࢿྉ͚ࣾʹ։࠵ͨ͠ςετίʔυͷ ษڧձࢿྉΛ֎෦ެ։͚ʹ࠶ฤͨ͠ͷͰ͢
Agenda1. Unittest جຊ֓೦2. ࣮ફ
Unittest جຊ֓೦
What isΓ͢͜ͱ͕Ͱ͖Δ࠷খͷίʔυϢχοτΛݕূ͢Δͷ• ԿΛʮΓ͢ʯͷ͔ʁ• ྫʣӬଓԽʹΞΫηε͢Δػೳɺ֎෦APIʹϦΫΤετΛൃߦ͢ΔػೳɺͳͲ• ݕূ݁Ռͷ߹൱ΛܾΊΔԿΒ͔ͷνΣοΫʢΞαʔγϣϯʣΛؚΉ
Why (1/3)ίʔυͷઃܭ͕վળ͞ΕΔʢ࠷ॏཁʣ• ͕ࣗ࡞ͬͨʮϢχοτʯ͕؆୯ʹςετͰ͖ͳ͍߹ɺԿ͔ͷઃܭ͕Πέͯͳ͍αΠϯ͔• e.g) ςετέʔεͷηοτΞοϓ͕ղɹˠɹඞཁҎ্ʹʮঢ়ଶʯΛ͔࣋ͪ͗͢ʁ• ͦͷϢχοτ͕աʹΛ࣋ͪ͗ͨ͢ઃܭʹͳ͍ͬͯΔ͔͠Εͳ͍ͱΘ͔Δ• ࣗͷઃܭΛʢΑΓϝϯςφϒϧͳߏʹ͢ΔͨΊʹʣධՁ͢Δ࡞ۀɺͱݴ͑Δ• ςετͮ͠Β͍ຊ൪ίʔυΛೝͯ͠ແཧ͘Γద߹ͤͯ͞͠·͏ͱɺϢχοτςετͷࢫຯ͕ݮΔ
Why (2/3)Ҿ͖ܧ͗ޙͷ୲ऀ͕ιϑτΣΞͷ༷Λཧղ͢ΔͨΊʹཱͭ• ։ൃͨ͠ίʔυ͕ຊʹՁΛͨΒ͢ͷ։ൃྃ࣌Ͱͳ͘อकϑΣʔζͰ͋Δ• ίʔυɺॻ࣌ؒ͘ΑΓಡ·ΕΔ࣌ؒͷํ͕ଟ͍• ෳࡶͰอकͮ͠Β͍ίʔυɺະདྷͷՁͷϙςϯγϟϧΛଛͳ͏• ͦͷίʔυϢχοτΛͲͷΑ͏ʹ͏͖͔ɺҙਤ༷ͨ͠ɾ͍ํΛਪͰ͖Δ• ※ͨͩ͠ɺdocstring ͦͷଞͷυΩϡϝϯτʢશମߏʗઃܭࢥʗσϓϩΠαΠΫϧͷखॱͳͲʣΛॆ࣮ͤ͞ΔͳͲɺଞʹҾ͖ܧ͗ͷͨΊͷඞཁཁૉ͍͘ΒͰ͋Δ
Why (3/3)Ϣχοτςετ͕ࣗಈςετͷૅʹͳΔʢ෭࣍తͳϝϦοτʣ• ࣗಈςετΛऔΓೖΕΔ͜ͱͰػೳվमͷݕূ͕εϐʔσΟʹͳΔ ʹՁΛੜΈग़͢αΠΫϧΛΑΓૣ͘ճͤΔ• ※ࣗಈςετͷલఏͱͯ͠ɺҙຯͷ͋ΔˍࣗಈԽ͍͢͠Ϣχοτςετ͕ॻ͚͍ͯΔ͜ͱ͕ॏཁʹͳΔ• ※ຊདྷతʹϢχοτςετ͚ͩͰͳ݁͘߹ςετߦͬͯɺϢʔεέʔεతʹҙຯΛཻ࣋ͭͷςετ͋ͬͨํ͕Α͍
Howʮ࣮͕ਖ਼͘͠ػೳ͍ͯ͠ΔͳΒ݁Ռ͜͏ͳΔͣʯΛݕূ• ಛఆ݅ʢҾʣΛ༩͑ͨ߹ͷΓΛݕূ• ςετର͕༻͢Δ֎෦ػೳΛఆ௨Γݺͼग़ͤΔ͔Ͳ͏͔• ఆ௨ΓʹίέΔ͜ͱʮਖ਼͠͞ʯͷݕূʹͳΔ
Howਖ਼ৗܥͷςετ
Howਖ਼ৗܥͷςετظ͢Δೖྗʗग़ྗΞαʔγϣϯ
Howҟৗܥͷςετ• int Ҏ֎ͷ߹ ValueError ྫ֎Λ্͛Δ ༷ΛՃ
Howҟৗܥͷςετ• int Ҏ֎ͷ߹ ValueError ྫ֎Λ্͛Δ ༷ΛՃܕҎ֎ͷέʔεʢ͜͜ͰจࣈྻܕʣΛਖ਼ৗʹͨ͘ΊͷόϦσʔγϣϯΛՃBEE ʹจࣈྻΛೖྗ͢Δͱ7BMVF&SSPSͰ͔ΕΔ͜ͱΛݕূ
Howผػೳͱͷ࿈ܞ͕͋Δؔͷ Ϣχοτςετ• ࿈ܞ͢ΔػೳΛμϛʔʹ ࠩ͠ସ͑Δ (mock)࣮ྫ͜ͷޙͷϋϯζΦϯηογϣϯͷ෦Ͱ
Mock ͱ … ʁʁʁʁʁ
Mockผػೳͱͷ࿈ܞΛؚΉؔͷ Ϣχοτςετ• ࿈ܞ͢ΔػೳΛμϛʔʹ ࠩ͠ସ͑Δ (mock)ϝΠϯϩδοΫ ֎෦"1*%#ΞΫηεɾɾɾ
Mockผػೳͱͷ࿈ܞ͕͋Δؔͷ Ϣχοτςετ• ࿈ܞ͢ΔػೳΛμϛʔʹ ࠩ͠ସ͑Δ (mock)ϝΠϯϩδοΫ ֎෦"1*%#ΞΫηεɾɾɾ͜ΕΛϢχοτςετ͍ͨ͠
Mockผػೳͱͷ࿈ܞ͕͋Δؔͷ Ϣχοτςετ• ࿈ܞ͢ΔػೳΛμϛʔʹ ࠩ͠ସ͑Δ (mock)ϝΠϯϩδοΫ ֎෦"1*%#ΞΫηεɾɾɾϝΠϯͷςετΛ͢Δ্Ͱɺ͍ͭ͜ΒͷӨڹ͕अຐ
Mockผػೳͱͷ࿈ܞ͕͋Δؔͷ Ϣχοτςετ• ࿈ܞ͢ΔػೳΛμϛʔʹ ࠩ͠ସ͑Δ (mock)ϝΠϯϩδοΫ ֎෦"1*NPDL%#ΞΫηεNPDLɾɾɾNBJOͷςετ࣌ͷΈɺμϛʔʹࠩ͠ସ͑ΔNPDL
Mockผػೳͱͷ࿈ܞ͕͋Δؔͷ Ϣχοτςετ• ࿈ܞ͢ΔػೳΛμϛʔʹ ࠩ͠ସ͑Δ (mock)ϝΠϯϩδοΫ ֎෦"1*NPDL%#ΞΫηεNPDLɾɾɾϝΠϯ͔Βར༻͢ΔTVCͷػೳʢؔͱ͔ʣ͕μϛʔͷΛฦ͢Α͏ʹઃఆ
Mockผػೳͱͷ࿈ܞ͕͋Δؔͷ Ϣχοτςετ• ࿈ܞ͢ΔػೳΛμϛʔʹ ࠩ͠ସ͑Δ (mock)ϝΠϯϩδοΫ ֎෦"1*NPDL%#ΞΫηεNPDLɾɾɾNBJOͷςετ࣌ͷΈɺμϛʔʹࠩ͠ସ͑ΔNPDL֎෦ӨڹΛഉআͯ͠ɺϝΠϯϩδοΫͷΈͷಈ࡞ΛݟΔ͜ͱ͕Ͱ͖Δ
Mockผػೳͱͷ࿈ܞ͕͋Δؔͷ Ϣχοτςετ• ࿈ܞ͢ΔػೳΛμϛʔʹ ࠩ͠ସ͑Δ (mock)ϝΠϯϩδοΫ ֎෦"1*NPDL%#ΞΫηεNPDLɾɾɾNPDLͨ͠ػೳΛʮͲ͏ͬͨͷ͔ʯ؍˞ݺͼग़͠ͷҾɺͦͷճͳͲ
Mock - Α͋͘ΔΘΕํ (1)ॴҦϢʔεέʔεʢػೳཁ݅ͷ࠷্ҐʹདྷΔΑ͏ͳͷʣΛ ςετ͢Δ߹ʹɺଞͷϞδϡʔϧͱͷ࿈ܞΛ֬ೝ͢Δ• ϝΠϯϩδοΫʹ͋ΔೖྗΛ༩͑ͨ߹ʹɺ ఆ௨ΓʹαϒػೳΛݺͼग़ͯ͠ ͍Δ͔Ͳ͏͔ʢMockͷݺͼग़͞ΕํΛݟΔʣϝΠϯϩδοΫ֎෦"1*%#ΞΫηεɾɾɾ͜͜ͷ࿈ܞ͕ఆ௨Γʹͳ͍ͬͯΔ͔Ͳ͏͔
Mock - Α͋͘ΔΘΕํ (2)ςετରʢ͜͜ͰػೳAʣࣗผͷ্Ґػೳ͔Βݺͼग़͞Εɺ ͳΜΒ͔ͷΓΛฦ͢ػೳͰ͋Δ߹• ػೳAͷґଘઌ(sub) Λ ϞοΫ্ͨ͠Ͱ ೖྗʗग़ྗύλʔϯΛ assert ͢Δػೳ" ֎෦"1*%#ΞΫηεfeature subɾɾɾϝΠϯϩδοΫmain͜͜ͷڍಈΛνΣοΫͯ͠ػೳ"Λݕূ͍ͨ͠ഉআ͍ͨ͠֎෦Өڹ
ࣄલࣝɿϋϯζΦϯͷલʹ
ϞδϡʔϧͷπϦʔͷԼʹσΟϨΫτϦΛͬͯ __init__.py Λஔ͢ΔͱɺPython (ͷ importlib) ͦͷσΟϨΫτϦΛʮϞδϡʔϧʯͱͯ͠ೝࣝ͢ΔΑ͏ʹͳΓ·͢ʢࢀߟʣɻιʔεπϦʔ͔Βࣗ࡞ιʔεΛ importʢ·ͨ from ~ importʣͰ͖ΔΑ͏ʹͳΓ·͢ɻimport Մೳimport ෆՄ>>> import my_module>>> from my_module import handler
Α͋͘ΔιʔεߏΑ͋͘Δͷࠨɻγϯϓϧͳ࡞ΓͳΒӈͷύλʔϯ͋ΔϧʔτσΟϨΫτϦ͔Β։ൃରͱςετΛ֤ϞδϡʔϧͱಉҰ֊ʹ։ൃରͱςετΛಉډ
sls ʹ͓͚ΔՃύοέʔδͷόϯυϧserverless-python-requirements Λ͏ͷ͕ศརʢϫʔΫγϣοϓͷιʔεͰ༻͍ͯ͠·͢• requirements.txt Pipfile ͷ༰Λউखʹόϯυϧͯ͘͠ΕΔ• requirements.txt Λ͍ͬͯΔ߹ಛʹՃઃఆෆཁ• ҙ໊শͰରԠՄೳɻ #Customize requirements file name ͷηΫγϣϯΛࢀর• layer ͷ࡞Ͱ͖Δ (#Lambda Layer ηΫγϣϯΛࢀর)serverless.yml هड़ྫ (Pipenv)
Tips / FAQ?
Tipsςετରͷʮ෭࡞༻(※)ʯΛߟྀ͢Δɻ෭࡞༻Ͱ͖Δ͚ͩݮΒ͢ or ہॴԽ͞Ε͍ͯΔํ͕ςετ͍͢͠• ೖྗ͕ಉ͡ͳΒ݁Ռৗʹಉ͡ʹͳΔΑ͏ʹ͢Δɻڍಈ͕༧ଌ͍͢͠ʢႈੑʣ• ෳࡶͳ෦ঢ়ଶʹґଘ͢ΔίʔυϢχοτͰ͖Δ͚ͩͳ͍ํ͕Α͍• εςʔτϑϧͳػೳશମͷҰ෦ʹԡ͠ࠐΉ• ֎෦ϦιʔεͷΞΫηεผϞδϡʔϧʹΓग़ͯ͠ɺͤΔ(※)… “ঢ়ଶ” ΛมԽͤ͞ɺͦͷޙͷॲཧ݁ՌʹӨڹΛ༩͑ΔԿΒ͔ͷॲཧΛࢦ͢ɻมͷೖɺετϨʔδDBͷΞΫηεͳͲ
Tips• ෦ঢ়ଶΛ࣋ͪɺڍಈ͕෦ঢ়ଶʹґଘ͢Δ• ʢඞવੑͷͳ͍ʣάϩʔόϧมͷґଘ• ಛఆΫϥεͷϝϯόʔมͷґଘ• ಉҰΫϥεͷಠཱͨ͠ʢΑ͏ʹݟ͑ΔʣϝιουؒͰݺͼग़͠ॱংʹґଘؔ• ֎෦ػೳͱͷ࿈ܞؚ͕·Ε͍ͯΔ• 3rd party ͷ API ͷݺͼग़͠• DB ΞΫηεʗετϨʔδΞΫηε
TipsʮৄࡉʯΛܾΊଧͪ͢ΔίʔυΛॻ͘ͱɺςετΛॻ͘ͱ͖ʹۤ࿑͢Δ• ৄࡉͱʁ• σʔλϕʔεͷબఆ … RDB/Key-Value/ΠϯϝϞϦ ??• ֎෦αʔϏεͱͷ௨৴ํ๏ͷৄࡉ … http/websocket/grpc/graphql ͳͲ• ϑΝΠϧΛอଘ͢ΔετϨʔδͷछྨʢϩʔΧϧ or S3?? σʔλϑΥʔϚοτͷ༷? ͳͲʣ
TipsʮৄࡉʯΛܾΊଧͪ͢ΔίʔυΛॻ͘ͱɺςετΛॻ͘ͱ͖ʹۤ࿑͢Δ• ͜ΕΒΛʮϢʔεέʔεʯͱͳΔϝΠϯϩδοΫʹϕλॻ͖͢Δͱ…• ϝΠϯϩδοΫͷςετͰʮৄࡉʯཁૉͷ۩ମతͳ͍ํܾΊଧͪ͢Δඞཁ͕ग़Δ• มߋʹରͯ͠؆୯ʹӨڹΛड͚ͯ͠·͏ɻςετͷ͕੬ऑʹͳΔ• Αͬͯʮৄࡉʯ͕ϝΠϯϩδοΫ͔Βݟ͑ͳ͍Α͏ʹ͢Δ• ۩ͷهड़Ͱͳ͘ɺநతͳΠϯλϑΣʔεͱͯ͠Γग़͢ɻ͜͏͢ΔͱϝΠϯϩδοΫͱͦͷςετ͕҆ఆ͍͢͠
Tips• ϢʔεέʔεΛཧ͠ɺͦΕΛදݱ͢Δநతͳ໊લͷΠϯλϑΣʔεΛΓग़͢• ࣮ํ๏ͷৄࡉʢྫ͑SQLͷॻ͖ํͱ͔ʣΛӅṭ͢Δ͜ͱͰɺϢʔεέʔεࣗମͷϩδοΫ෦తͳৄࡉ༷ͷมߋʹରͯ͠ӨڹΛड͚ͮΒ͘ͳΔ• ʮϕλॻ͖ʯ͢ΔΑΓɺػೳΛநతͳΠϯλϑΣʔεʹΓग़ͯ͠ɺͦͷΠϯλϑΣʔεʹґଘͤ͞Δํ͕·͍͠• Factory method / Abstract Factory ύλʔϯ• ґଘੑͷೖʢDependency Injectionʣ
FAQ?ʮςετۦಈ։ൃ (TDD) ʯςετͷٕ๏Ͱͳ͍• TDD։ൃͷख๏• ઌʹϢχοτςετΛॻ͖࢝ΊΔ͜ͱͰɺઃܭͷϑΟʔυόοΫΛਝʹɺ͔ͭܧଓతʹಘΔ͜ͱ͕Ϟνϕ
FAQ?xxx-local γϦʔζͱ͔ɺlocalstackͬͯͲ͏ͳΜʁ• ʢݸਓతҙݟʣ૯ධͱͯ͠ɺςετ༻్ͱͯ͠ݟΔͳΒΠϚΠνɻͲ͏ͯ͠ඞཁͳཧ༝͕ग़ͨͱ͖ʹ͚ͩݕ౼͖͢ → YAGNI ͷݪଇɻʮૣ͗ͨ͢࠷దԽʯʹ͍͚ؕͬͯͳ͍• localstack Λςετͷखஈͱͯ͠Ξςʹ͗͢͠Δͱ…• ςετຊ൪ίʔυͷҰ෦ͳͷͰɺߏ͕มΘͬͨΒ localstack ਵ͢Δඞཁ͕͋Δ• localstack ͷҡ࣋ཧ͕อकίετʹͳͬͯ͠·͏ʢͦͯ͠ϝϯς͞Εͳ͍ෛ࠴ίʔυʣ
FAQ?doctest ͬͯͲ͏ͳΜʁ• ຊ൪ίʔυʹॻ͔Εͨ”ಈ࡞͢Δ”༷ॻͱͯ͠ɺҙٛे͋Γͦ͏ʢಠཱͨ͠υΩϡϝϯτ์ஔ͞Ε͍͢ͷͰɺίʔυϕʔεͱಉ͡ॴʹ͋ΔυΩϡϝϯτͰ͋Δ͜ͱʹՁ͕͋Δʣ• ͱ͍͑ɺΤσΟλͷิ docstring ͷ࣮ߦίϯςΩετͳͲҙࣝ͢Δඞཁ͕͋Γͦ͏ͩ͠ɺ୯७ʹॻ͖ͮΒ͍ͷͰݱ࣌Ͱ͋Μ·ΓϙδςΟϒͳҹͳ͍
FAQ?xxx-local γϦʔζͱ͔ɺlocalstackͬͯͲ͏ͳΜʁ• lambda-local• ݸਓతʹ͋Μ·Γ༗ޮͳ్͍͕ͳ͍ҹ• ϥϯλΠϜݻ༗ͷΤϥʔέʔε·Ͱݟ͍ͨͳΒΞϦʁ…ͰͦΕ࣮ػͰͬʢུ
FAQ?xxx-local γϦʔζͱ͔ɺlocalstackͬͯͲ͏ͳΜʁ• dynamodb-local• ख͔࣮ܰͭࡍʹΞΫηεͰ͖Δಈ࡞ݕূڥͱͯ͠ΞϦɻFunctional Test ʹ͑Δ͔ʁ• ݸਓతʹΫΤϦܗࣜΛಈ࡞ݕূ͢Δͱ͖ʹ͋ͬͨΒ͍͍ఔɻσʔλͷग़͠ೖΕΛ͏ؔͷςετͰ͋Εɺී௨ʹMockͰࣄΓΔ• εΩʔϚมΘͬͯςετͷҰ؏ੑΛอ͍ͪͨͳΒ…ͨͩɺεΩʔϚมߋDBΈ͔͠ʢLSI/GSIՃͷΈͰʣطଘΫΤϦʹӨڹͳͩ͠ͱࢥ͏ͷͰɺҡ࣋ͷखؒʹݟ߹͏ͱߟ͑ͯͳ͍
FAQ?xxx-local γϦʔζͱ͔ɺlocalstackͬͯͲ͏ͳΜʁ• localstack• ※ͬͨ͜ͱͳ͍ͷͰͳΜͱͳ͘ͷҹΛճ͠·͢• ϩʔΧϧͰͷݕূʢvalidateͰͳ͘ɺPoCతͳҙຯͷํʣʹ͏ͷɺͱ͍͏ఔ• ςετຊ൪ίʔυͷҰ෦ͳͷͰɺίʔυͷมߋʹ͍ localstack ҡ࣋ཧ͕ඞཁʹͳΔɻ Ҿ͖ܧ͗ίετɾอकίετ্͕͕Δ͕ɺͦΕʹݟ߹͏΄ͲͷϝϦοτ͕͋·Γݟ͍ͩͤͳ͍ҹ
ࢀߟ• ॻ੶• Ϧʔμϒϧίʔυ ―ΑΓྑ͍ίʔυΛॻͨ͘ΊͷγϯϓϧͰ࣮ફతͳςΫχοΫ• ςετۦಈ։ൃ• ςετۦಈPython
࣮ફ
※ϋϯζΦϯηογϣϯ
ࢀߟ
ςετΛࢧԉ͢Δύοέʔδ (1/2)• tmpfile … Pythonඪ४ύοέʔδɻϑΝΠϧͷI/OΛςετ͢Δඞཁ͕͋Δ߹ʹɺςετதͷΈ༻͢ΔҰ࣌తͳϑΝΠϧ/σΟϨΫτϦΛ࡞ΕΔ• nose … unittestΛͬͱศརʹ͢Δͭ• pytest … unittestΛͬͱศརʹ͢Δͭ• coverage … ΧόϨοδϨϙʔτͷੜ (nose, pytest ͳͲʹϓϥάΠϯՄೳ)• tox … ෳόʔδϣϯͷPythonͰςετΛ࣮ࢪ͢Δͱ͖ʹ͏
ςετΛࢧԉ͢Δύοέʔδ (2/2)• httpretty … ੜͷHTTPϦΫΤετΛϞοΫ͢Δʢ࣮ྫʣ• moto … boto3 ༻ͷMockʢBatchWriterͳͲҰ෦ඇରԠ͋Γʣ• aws sam cli … Lambda ͷ event ҾͷαϯϓϧೖྗΛखૣ͘ੜ͢Δͷʹ͑Δ• serverless-dynamodb-local … Serverless ϓϥάΠϯɻDynamoDB ͷϩʔΧϧಈ࡞ݕূʹศར
See Also• Python document: unittest —- ϢχοτςετϑϨʔϜϫʔΫ• ςετۦಈ։ൃ• CAREER SKILLSɹιϑτΣΞ։ൃऀͷશΩϟϦΞΨΠυ• ୈ29ষ ςετۦಈ։ൃͱϢχοτςετ• ୈ32ষ σόοά