Upgrade to Pro — share decks privately, control downloads, hide ads and more …

リーダブルテストコード / #vstat

リーダブルテストコード / #vstat

「リーダブルなテストコードについて考えよう ~VeriServe Test Automation Talk No.3~」で使用したスライドです。
https://veriserve-event.connpass.com/event/243280/

<参考リンク>

Twitter https://twitter.com/jnchito

Blog https://blog.jnito.com/

Qiita https://qiita.com/jnchito

プロを目指す人のためのRuby入門[改訂2版]
https://gihyo.jp/book/2021/978-4-297-12437-3

Everyday Rails - RSpecによるRailsテスト入門
https://leanpub.com/everydayrailsrspec-jp/

https://twitter.com/jnchito/status/1552196880524013569

https://twitter.com/jnchito/status/1539100857991852032

https://twitter.com/jnchito/status/1539100861947064320

Clean Test Code Revised (by Shinichi Maeshima)
https://speakerdeck.com/willnet/clean-test-code-revised

【初心者向け】テストコードの方針を考える(何をテストすべきか?どんなテストを書くべきか?)
https://qiita.com/jnchito/items/2a5d3e15761fd413657a

なぜテストを書くの?(または書かないの?) 〜テストコードの7つの役割〜
https://speakerdeck.com/jnchito/number-tamarubykaigi01

テストコードの期待値はDRYを捨ててベタ書きする ~テストコードの重要な役割とは?~
https://qiita.com/jnchito/items/eb3cfa9f7db752dcb796

僕がRSpecでsubjectを使わない理由
https://blog.jnito.com/entry/2021/10/09/105651

48a913a2e3bb5e68aae6f73079648e84?s=128

Junichi Ito

July 27, 2022
Tweet

More Decks by Junichi Ito

Other Decks in Programming

Transcript

  1. Ϧʔμϒϧςετίʔυ גࣜձࣾιχοΫΨʔσϯɿҏ౻३Ұ 2022-7-27 VeriServe Test Automation Talk No.3

  2. https://twitter.com/jnchito/status/1552196880524013569 ͍͖ͳΓ༨ஊ ࠓ೔΄Μͱ͏ʹ͋ͬͨ࿩

  3. ͍͖ͳΓ༨ஊ φΠελΠϛϯάʂ!

  4. 5 Hello, world! " • ҏ౻ ३Ұ • גࣜձࣾιχοΫΨʔσϯͷRailsϓϩάϥϚ •

    ϑΟϤϧυϒʔτΩϟϯϓͷϝϯλʔ • ฌݿݝ੢࿬ࢢࡏॅʢϦϞʔτϫʔΫྺ10೥ʣ • ςετίʔυྺ 15೥Ҏ্ʢJUnit → NUnit → RSpecʣ !KODIJUP CMPHKOJUPDPN
  5. 6 Qiita • ϢʔβʔϥϯΩϯά1Ґʢ2022೥7݄ݱࡏʣ • QiitaදজϓϩάϥϜ །ҰͷDIAMONDड৆ऀ #

  6. 7 ஶॻ ʮϓϩΛ໨ࢦ͢ਓͷͨΊͷRubyೖ໳ʯ ‣ ௨শɾνΣϦʔຊ$ ‣ ग़൛ࣾɿٕज़ධ࿦ࣾ ‣ 2017೥11݄ ୈ̍൛ൃച

    ‣ 2021೥12݄ վగ̎൛ൃചʢRuby 3.0ʹରԠʣ 3VCZͱ5%%͕ಉ࣌ʹֶ΂·͢ʂ ͦͯ͠ʜʜ ㊗ॏ൛ग़དྷʂʢࠓ೔࿈བྷ͕དྷ·ͨ͠&ʣ ͡Ύ͏͸Μ͠Ύ͍ͬͨ
  7. 8 ༁ॻ ʮEveryday Rails - RSpecʹΑΔRailsςετೖ໳ʯ ‣ Aaron Sumner ஶ

    ‣ ग़൛ࣾɿLeanpub ‣ ిࢠॻ੶ͱͯ͠ൃചத ‣ 2014೥2݄ 1stϦϦʔε ‣ 2022೥1݄ Rails 7.0ʹରԠ 34QFDͰ3BJMTΞϓϦΛςετ ͍ͨ͠ਓ͸ͪ͜ΒΛͲ͏ͧʂ
  8. 9 ຊ೔͓࿩͢͠Δ͜ͱ • ςετίʔυʹ͓͍ͯɺա౓ͳDRY͸ಡΈ΍͢͞ͷఢ • ݡͯ͘ϩδΧϧͳςετίʔυΑΓɺ୭Ͱ΋ಡΊΔ۪௚ͳςετίʔυΛʂ • ೴಺ϝϞϦΛ࢖Θͳ͍ςετίʔυ΄ͲϦʔμϒϧ • ࣮ߦՄೳͳAPIυΩϡϝϯτͩͱࢥͬͯςετίʔυΛॻ͜͏

    • ϓϩάϥϚ͕ࣗ෼Ͱॻ͘Ϣχοτςετͷ࿩͕ϝΠϯͰ͢
  9. 10 ඞཁͳࣄલ஌ࣝ • αϯϓϧίʔυ͸RSpecʢRubyʣͰॻ͖·͢ • ͕ɺςετίʔυΛॻ͍ͨܦݧ͕͋Ε͹͍͍ͩͨཧղͰ͖Δ͸ͣ • ͦ͏ɺϦʔμϒϧͳςετίʔυͳΒͶʂ • จࣈ͕খ͍͞৔߹͸ɺ͖ͬ͞πΠʔτͨ͠εϥΠυΛ։͍͍ͯͩ͘͞

    if You.cannot.read_this? visit "https://speakerdeck.com/jnchito/readable-test-code-vstat" end
  10. ͱ͍͏Θ͚ͰɺͦΖͦΖຊฤ΁

  11. Ϧʔμϒϧςετίʔυ

  12. Q. ͜Μͳܦݧ͸͋Γ·ͤΜ͔ʁ

  13. https://twitter.com/jnchito/status/1539100857991852032

  14. Α͏Θ͔ΒΜͳΒ approveͪ͠ΌμϝͰ͢'

  15. https://twitter.com/jnchito/status/1539100861947064320 ͖ͬ͞ͷπΠʔτͷଓ͖

  16. ςετίʔυʹ͓͍ͯɺա౓ͳ DRY͸ಡΈ΍͢͞ͷఢ DRY = Don't Repeat YourselfͷུͰίʔυ΍σʔλͷॏෳΛͳͤ͘ɺͱ͍͏ݪଇ

  17. ςετίʔυʹ͓͍ͯɺա౓ͳ DRY͸ಡΈ΍͢͞ͷఢ DRY = Don't Repeat YourselfͷུͰίʔυ΍σʔλͷॏෳΛͳͤ͘ɺͱ͍͏ݪଇ ੲͷ๻Ͱ͢ ը૾͸ʮѱ॥؀ը૾δΣωϨʔλʯͰ࡞੒

  18. 19 Ϧʔμϒϧͳςετίʔυͷ৚݅ ✅ υΩϡϝϯτͷΑ͏ʹ্͔ΒԼʹૉ௚ʹಡΈԼͤΔ ✅ ม਺Λ࢖Θͣʹจࣈྻ΍਺஋Λϕλॻ͖͢Δ ✅ ڽͬͨςΫχοΫΛཚ༻͠ͳ͍ ❌ ϧʔϓॲཧ΍৚݅෼ذ͕සൃ͢Δʢࢹઢ্͕Լͨ͠Γδϟϯϓͨ͠Γ͢Δʣ

    ❌ ਺஋΍จࣈྻͷΑ͏ͳ୯७ͳσʔλ·Ͱશ෦ม਺ʹ֨ೲ͞Ε͍ͯΔ ❌ shared examples΍subjectͳͲɺςεςΟϯάFWݻ༗ͷػೳΛཚ༻͍ͯ͠Δ ൓ରʹ͢ΔͱʮಡΈʹ͍͘ςετίʔυʯͷͰ͖͕͋Γʂʂ ϑϨʔϜϫʔΫ
  19. ݡͯ͘ϩδΧϧͳςετίʔυΑΓɺ ୭Ͱ΋ಡΊΔ۪௚ͳςετίʔυΛʂ ඇΤϯδχΞͰ΋ʂ

  20. ࣮ࡍʹૺ۰ͨ͠ςετίʔυͰઆ໌

  21. 22 ࠓճςετ͢Δϝιου = UserΫϥεͷageϝιου def age today = Date.today this_years_birthday

    = Date.new( today.year, birth_date.month, birth_date.day) ret = today.year - birth_date.year if today < this_years_birthday ret -= 1 end ret end • ࢓༷΍ϩδοΫ͸͋͑ͯઆ໌͠·ͤΜ ※ աڈʹ࣮ࡍʹίʔυϨϏϡʔͨ͠ϓϩάϥϜͱͦͷςετίʔυΛҰ෦վม͍ͯ͠·͢
  22. 23 ๻͕ϨϏϡʔͨ͠ςετίʔυ͸͜Ε let(:user) { create(:user, birth_date: Faker::Date.birthday) } let(:this_year) {

    Date.today.year } let(:this_years_birthday) { Date.new(this_year, user.birth_date.month, user.birth_date.day) } let(:age) { this_year - user.birth_date.year } context "஀ੜ೔Ҏ߱ͷ৔߹" do it "஀ੜ೔Λա͗ͨ೥ྸ͕ฦΔ" do travel_to(this_years_birthday) expect(user.age).to eq(age) end end context "஀ੜ೔Ҏલͷ৔߹" do it "஀ੜ೔લͷ೥ྸ͕ฦΔ" do travel_to(this_years_birthday.yesterday) expect(user.age).to eq(age - 1) end end ໰୊ɿageϝιου͸ͲΜͳ࢓༷ʁ
  23. https://twitter.com/jnchito/status/1539100857991852032 ࠶ܝ

  24. ͖ͬ͞ͷςετίʔυɺapprove͢Δʁ ✅:&4 ❌/0

  25. ๻͸͠·ͤΜ' ❌/0

  26. ͳͥͳΒϦʔμϒϧͰ͸ͳ͍͔Β ❌/0

  27. ςετίʔυͷ͔Β͘ΓΛಡΈղ͘ ⚠ ղઆ͕ඞཁͳςετίʔυʹͳͬͯΔ࣌఺Ͱͦ΋ͦ΋͓͔͍͠Αʂ

  28. let(:user) { create(:user, birth_date: Faker::Date.birthday) } let(:this_year) { Date.today.year }

    let(:this_years_birthday) { Date.new(this_year, user.birth_date.month, user.birth_date.day) } let(:age) { this_year - user.birth_date.year } context "஀ੜ೔Ҏ߱ͷ৔߹" do it "஀ੜ೔Λա͗ͨ೥ྸ͕ฦΔ" do travel_to(this_years_birthday) expect(user.age).to eq(age) end end context "஀ੜ೔Ҏલͷ৔߹" do it "஀ੜ೔લͷ೥ྸ͕ฦΔ" do travel_to(this_years_birthday.yesterday) expect(user.age).to eq(age - 1) end end ςετ͢Δϝιου ςετ͢Δϝιου let(:x) { 1 } ɹ x = 1 ͸ Έ͍ͨͳΠϝʔδ travel_to ͸Ҿ਺Ͱࢦఆͨ͠೔෇ʹγεςϜ೔෇Λมߋ͢Δϝιου ←
  29. let(:user) { create(:user, birth_date: Faker::Date.birthday) } let(:this_year) { Date.today.year }

    let(:this_years_birthday) { Date.new(this_year, user.birth_date.month, user.birth_date.day) } let(:age) { this_year - user.birth_date.year } context "஀ੜ೔Ҏ߱ͷ৔߹" do it "஀ੜ೔Λա͗ͨ೥ྸ͕ฦΔ" do travel_to(this_years_birthday) expect(user.age).to eq(age) end end context "஀ੜ೔Ҏલͷ৔߹" do it "஀ੜ೔લͷ೥ྸ͕ฦΔ" do travel_to(this_years_birthday.yesterday) expect(user.age).to eq(age - 1) end end travel_to ͸Ҿ਺Ͱࢦఆͨ͠೔෇ʹγεςϜ೔෇Λมߋ͢Δϝιου ςετ͢Δϝιου ςετ͢Δϝιου let(:x) { 1 } ɹ x = 1 ͸ Έ͍ͨͳΠϝʔδ ← ←ϥϯμϜʹܾ·ΔUserͷੜ೥݄೔ ←γεςϜ೔෇͔Βʮࠓ೥ʯΛऔಘʢ2022ͳͲʣ ←ಈతʹੜ੒͞ΕΔʮࠓ೥ͷ஀ੜ೔ʯ ←ಈతʹੜ੒͞ΕΔʮࠓ೥ͷ೥ྸʯ ্Ͱੜ੒ͨ͠ʮࠓ೥ͷ೥ྸʯͱageϝιουͷ໭Γ஋͕Ұக͢Δ͜ͱΛݕূ ←γεςϜ೔෇Λʮࠓ೥ͷ஀ੜ೔ͷલ೔ʯʹมߋ ্Ͱੜ੒ͨ͠ʮࠓ೥ͷ೥ྸʯΑΓ1ࡀए͘ͳΔ͜ͱΛݕূ ←γεςϜ೔෇Λʮࠓ೥ͷ஀ੜ೔ʯʹมߋ
  30. ……+

  31. Θ͔Δ͜ͱɿ೴಺ϝϞϦΛফඅ͢Δ ςετίʔυ͸ϦʔμϒϧͰͳ͍ ࢀߟจݙɿClean Test Code Revised (by Shinichi Maeshima) https://speakerdeck.com/willnet/clean-test-code-revised

  32. ͦ͜Ͱɺม਺Λͳͯ͘͠ΈΔ

  33. let(:user) { create(:user, birth_date: "1977/07/17".to_date) } context "஀ੜ೔Ҏ߱ͷ৔߹" do it

    "஀ੜ೔Λա͗ͨ೥ྸ͕ฦΔ" do travel_to("2022/07/17".to_date) expect(user.age).to eq(45) end end context "஀ੜ೔ΑΓҎલͷ৔߹" do it "஀ੜ೔લͷ೥ྸ͕ฦΔ" do travel_to("2022/07/16".to_date) expect(user.age).to eq(44) end end ςετ͢Δϝιου ςετ͢Δϝιου ্͔ΒԼʹಡΊΔΑ͏ʹͳͬͨ఺ʹ΋஫໨ʂ
  34. let(:user) { create(:user, birth_date: "1977/07/17".to_date) } context "஀ੜ೔Ҏ߱ͷ৔߹" do it

    "஀ੜ೔Λա͗ͨ೥ྸ͕ฦΔ" do travel_to("2022/07/17".to_date) expect(user.age).to eq(45) end end context "஀ੜ೔ΑΓҎલͷ৔߹" do it "஀ੜ೔લͷ೥ྸ͕ฦΔ" do travel_to("2022/07/16".to_date) expect(user.age).to eq(44) end end ςετ͢Δϝιου ςετ͢Δϝιου ্͔ΒԼʹಡΊΔΑ͏ʹͳͬͨ఺ʹ΋஫໨ʂ ←ੜ೥݄೔͸1977೥7݄17೔ ←γεςϜ೔෇Λ2022೥7݄17೔ͱ͢Δ ageϝιου͸ʮ45ࡀʯΛฦ͢ ←γεςϜ೔෇Λ2022೥7݄16೔ͱ͢Δ ageϝιου͸ʮ44ࡀʯΛฦ͢ ্͔ΒԼʹಡΊΔΑ͏ʹͳͬͨ఺ʹ΋஫໨ʂ
  35. &

  36. ೴಺ϝϞϦͷফඅΛ཈͑Δ΄Ͳ ϦʔμϒϧͳςετίʔυʹͳΔ ࢀߟจݙɿClean Test Code Revised (by Shinichi Maeshima) https://speakerdeck.com/willnet/clean-test-code-revised

  37. 38 ςετίʔυ͸Ұछͷ࢓༷ॻ def age today = Date.today this_years_birthday = Date.new(

    today.year, birth_date.month, birth_date.day) ret = today.year - birth_date.year if today < this_years_birthday ret -= 1 end ret end ͋ ͋ • ςετίʔυΛݟΕ͹ϝιουͷৼΔ෣͍͕͙͢Θ͔Δͷ͕ཧ૝ context "஀ੜ೔Ҏ߱ͷ৔߹" do it "஀ੜ೔Λա͗ͨ೥ྸ͕ฦΔ" do travel_to("2022/07/17".to_date) expect(user.age).to eq(45) end end context "஀ੜ೔ΑΓҎલͷ৔߹" do it "஀ੜ೔લͷ೥ྸ͕ฦΔ" do travel_to("2022/07/16".to_date) expect(user.age).to eq(44) end end RubyΛ஌Βͳ͍ਓͰ΋ςετίʔυ ͳΒΘ͔Δɺͱ͍͏ঢ়ଶΛ໨ࢦͦ͏ #
  38. 39 APIυΩϡϝϯτͷαϯϓϧίʔυͱߟ͑ํ͸ಉ͡ APIυΩϡϝϯτ΋αϯϓϧίʔυ͸ϕλॻ͖͕جຊ ग़య: https://docs.ruby-lang.org/ja/latest/method/File/s/basename.html

  39. 40 ΋͠΋APIυΩϡϝϯτ͕ϕλॻ͖͡Όͳ͔ͬͨΒ…… ,

  40. ࣮ߦՄೳͳAPIυΩϡϝϯτͩͱ ࢥͬͯςετίʔυΛॻ͜͏ ςετίʔυ͸ϓϩάϥϜ ͡Όͳͯ͘υΩϡϝϯτʂ

  41. ͦͷଞͷτϐοΫ

  42. 43 E2EςετͰ΋ߟ͑ํ͸ಉ͡ click_link user.name expect(page).to have_text "#{user.name}ͷ೔ه" ❌ ը໘্ͷཁૉΛม਺Ͱࢦఆ͢Δ click_link

    "͋Γ͢" expect(page).to have_text "͋Γ͢ͷ೔ه" ✅ ը໘্ͷཁૉΛϕλॻ͖Ͱࢦఆ͢Δ ม਺Λଟ༻͢ΔͱνϦπϞͰ͡Θ͡Θͱ೴಺ϝϞϦΛফඅ͠·͢……ʂʂ ※ E2Eςετ = End to Endςετɻϒϥ΢βૢ࡞ΛࣗಈԽ͠ɺγεςϜશମͷಈ࡞Λݕূ͢Δ
  43. 44 ·ͩ͋ΔϦʔμϒϧʹ͢Δίπ • ࣮ࡍͷϢʔεέʔεʹ͍ۙςετσʔλΛ࢖༻͢Δ ‣ ʮ͋͋͋ʯʮςετςετʯʮϢʔβʔ̍ʯΈ͍ͨͳςετσʔλ͸NG • describe/context/itͷઆ໌Λஸೡʹॻ͘ ‣ ʮit

    "ద੾ͳ஋Λฦ͢"ʯΈ͍ͨͳ۩ମੑͷͳ͍આ໌͸NG • ͢΂ͯͷ৘ใ͕̍ը໘ʹऩ·Δςετίʔυ͕ཧ૝ ‣ ্ԼεΫϩʔϧ͕සൟʹൃੜͨ͠ΓɺଞͷϑΝΠϧΛݟʹߦ͔ͳ͖Ό͍͚ͳ͍ͷ͸NG • DRYېࢭ͸͋͘·ͰݪଇɻదٓϝϦοτͱσϝϦοτΛఱṝʹ͔͚Δ ‣ ʮ໌֬ͳϝϦοτ͕͋ΔDRYʯ΍ʮՄಡੑΛଛͳΘͳ͍ந৅Խʯ·Ͱ์غ͢Δͷ͸NG
  44. 45 ৄ͘͠͸WebͰʂ • ʲॳ৺ऀ޲͚ʳςετίʔυͷํ਑Λߟ͑ΔʢԿΛςετ͢΂͖͔ʁͲΜͳςετΛॻ͘΂͖͔ʁʣ https://qiita.com/jnchito/items/2a5d3e15761fd413657a • ͳͥςετΛॻ͘ͷʁʢ·ͨ͸ॻ͔ͳ͍ͷʁʣ ʙςετίʔυͷ7ͭͷ໾ׂʙ https://speakerdeck.com/jnchito/number-tamarubykaigi01 •

    ςετίʔυͷظ଴஋͸DRYΛࣺͯͯϕλॻ͖͢Δ ʙςετίʔυͷॏཁͳ໾ׂͱ͸ʁʙ https://qiita.com/jnchito/items/eb3cfa9f7db752dcb796 • ๻͕RSpecͰsubjectΛ࢖Θͳ͍ཧ༝ https://blog.jnito.com/entry/2021/10/09/105651
  45. ·ͱΊ

  46. 47 ࠓ೔ͷ͓͞Β͍ subject { "ςετίʔυ" } • subject ʹ͓͍ͯɺա౓ͳDRY͸ಡΈ΍͢͞ͷఢ •

    ݡͯ͘ϩδΧϧͳ subject ΑΓɺ୭Ͱ΋ಡΊΔ۪௚ͳ subject Λʂ • ೴಺ϝϞϦΛ࢖Θͳ͍ subject ΄ͲϦʔμϒϧ • ࣮ߦՄೳͳAPIυΩϡϝϯτͩͱࢥͬͯ subject Λॻ͜͏ ͋ΕʁͳΜ͔ಡΈͮΒ͍ͳ…… subject { "foo" } ɹ subject = "foo" ※ɹɹɹɹɹɹɹɹɹɹɹ ͸ ɹɹɹɹɹɹ Έ͍ͨͳΠϝʔδ
  47. ͓ͬͱɺ͍͢·ͤΜʂ ͍ͭɺDRYʹॻ͖ͨ͘ͳΔΫη͕-

  48. ͪΌΜͱϕλॻ͖͠·͢.

  49. 50 ࠓ೔ͷ͓͞Β͍ • ςετίʔυʹ͓͍ͯɺա౓ͳDRY͸ಡΈ΍͢͞ͷఢ • ݡͯ͘ϩδΧϧͳςετίʔυΑΓɺ୭Ͱ΋ಡΊΔ۪௚ͳςετίʔυΛʂ • ೴಺ϝϞϦΛ࢖Θͳ͍ςετίʔυ΄ͲϦʔμϒϧ • ࣮ߦՄೳͳAPIυΩϡϝϯτͩͱࢥͬͯςετίʔυΛॻ͜͏

  50. https://twitter.com/jnchito/status/1539100857991852032 ࠶ܝʢ2ճ໨ʣ

  51. ϦʔμϒϧͳςετίʔυΛॻ͍ͯ ࣗ৴Λ΋ͬͯϤγʂͯ͠΋Β͍·͠ΐ͏ -(5. jnchito

  52. ͝ਗ਼ௌ !KODIJUP CMPHKOJUPDPN ɹɹɹ͋Γ͕ͱ͏͍͟͝·ͨ͠