クックパッド17day技術インターンシップの前半、TDDへんの講義資料です
TDD: Test DrivenDevelopment2017-08-08@moro ॾڮګհ
View Slide
❌ ςεςΟϯάϑϨʔϜϫʔΫͷ͍ํ ɹɹɹ(࣮श࣌ʹݸผʹฉ͍͍ͯͩ͘͞)⭕ TDDͷૂ͍⭕ TDDΛऔಘ͢ΔͨΊͷΧλ͖ΐ͏͢͜ͱ
What is TDD?
TDD =Test Driven Development
ςετۦಈ։ൃɺϓϩάϥϛϯάதͷෆ҆Λཧ͢Δख๏ͩɻෆ҆ͱʮ͜ΕࠔͳͳͷͰɺ࠷ॳ͔Βͯ͢Λݟ௨ͤΔΘ͚Ͱͳ͍ʯͱ͍͏ਅͬͳײ֮ͷ͜ͱͩɻུ5%%ϓϩάϥϛϯάதͷઃܭஅͱϑΟʔυόοΫͷؒʹ͋ΔΪϟοϓΛೝࣝ͢Δ͜ͱͰ͋ΓɺͦͷΪϟοϓΛίϯτϩʔϧ͢Δٕ๏Ͱ͋Δɻςετۦಈ։ൃೖ
‣ ιϑτΣΞΛ࣮ߦ͠ɺͦͷ݁ՌΛ֬ೝͯ࣍͠ʹॻ͘ίʔυΛܾΊΔɻ‣ ςεςΟϯάϑϨʔϜϫʔΫ͕ͦΕΛࢧԉͯ͘͠ΕΔɻ‣ ࣮ߦ͢ΔͨΊͷ४උ(σʔληοτΞοϓͳͲ)‣ ࣮ߦ݁Ռͷࣗಈ֬ೝ‣ ܁Γฦ࣮͠ߦͷࢧԉϑΟʔυόοΫ
Why TDD?
ʮಈ࡞͢Δ͖Ε͍ͳίʔυʯɺϩϯɾδΣϑϦʔζͷ͜ͷ؆ܿͳݴ༿ɺ5%% ςετۦಈ։ൃͷඪͰ͋Δɻಈ࡞͢Δ͖Ε͍ͳίʔυɺ͋ΒΏΔཧ༝ͰՁ͕͋Δɻᴷ,FOU#FDL
ಈ࡞͢Δɺ͖Ε͍ͳίʔυ͖Ε͍Ԛ͍(͙͢ʹ)ಈ͔ͳ͍ ಈ࡞͢Δೋͭͷಓ͕͋Δt-wada: says
‣ TDDͷʮςετʯͱ͍͏χϡΞϯε͕ಠΓา͖ͨ݁͠Ռɺ ʰݟΔ͖ΦϒδΣΫτͷৼΔ͍ʱͩͱ͍͏ཱ͕ݱΕͨɻ‣ (ࢲ͡͠Μɺ࣮རͱͯ͠ͷҧ͍ͳ͍ͱ͍͏ཱ)‣ ։ൃΛۦಈ͢ΔɺͦͷͨΊʹظ͢Δʮ;Δ·͍ʯΛ ςετίʔυͰදݱ͠ɺ࣮ߦ͠ɺϑΟʔυόοΫΛಘΔɻ‣ assertͳΒTDD, expectͳΒBDDͱ͍͏Ͱͳ͍ɻFYI: TDDͱBDDͷҧ͍
How to TDD?
How to learn TDD?
कɾഁɾ
‣ Ϩου৽͍͠ίʔυΛॻ͘લʹɺ·ࣦͣഊ͢ΔϢχοτςετΛॻ͘ɻ৽͍͠ίʔυͷҙਤΛςετͰࣔ͢ΜͩɻϨουઃܭʹ͍͔ͭͯͬ͠Γߟ͑Δग़ൃͩɻ‣ άϦʔϯͱʹ͔͘ςετʹޭ͢ΔίʔυΛॻ͘ɻ࣮ͷ࠷ऴܗΛࢥ͍ඳ͚ΔͳΒҰؾʹ্͛ͯ͠·͍͍͑ɻͦ͜·Ͱͷࣗ৴͕ͳ͚Εɺ·ͣςετʹޭ͢Δఔͷ࣮͚ͩͰߏΘͳ͍ɻ‣ ϦϑΝΫλϦϯά࣮Λݟ͢ɻςετΛޭͤ͞ΔͨΊʹΒ͔᤻ͨ͠ͷʑΛ͖Ε͍ʹআ͠Α͏ɻॏෳΛऔΓআ͘ɻແବΛল͘ɻҙਤΛ͖ͬΓͤ͞Δɻग़དྷΔݶΓ໌ྎͰ͔Γ͍͢ίʔυʹ͠Α͏ɻΞδϟΠϧαϜϥΠୈষςετۦಈ։ൃ
5%%ͷαΠΫϧ1.࣍ͷඪΛߟ͑Δ2.ͦͷඪΛࣔ͢ςετΛॻ͘3.ͦͷςετΛ࣮ߦࣦͯ͠ഊͤ͞Δ 3FE4.తͷίʔυΛॻ͘5.Ͱॻ͍ͨςετΛޭͤ͞Δ (SFFO6.ςετ͕௨Δ··ͰϦϑΝΫλϦϯάΛߦ͏ 3FGBDUPS7.̍ʙΛ܁Γฦ͢t-wada: says
͖Ε͍Ԛ͍(͙͢ʹ)ಈ͔ͳ͍ ಈ࡞͢ΔRedGreenRefactoring5%%ͱԫۚͷճసt-wada: says
͓࣌ܭΦϒδΣΫτ
‣ ςεςΟϯάϑϨʔϜϫʔΫ ͦͷଞڥͪΌΜͱಈ͔͘࠷ॳͷඪΛߟ͑Δ
$ vim spec/tokei_spec.rb```describe '時計' dospecify { expect(true).to be true }end``````$ bundle exec rspec.Finished in 0.00097 seconds (files took 0.09778 seconds to load)1 example, 0 failures```࠷ॳͷҰา
‣ RSpec͕ͪΌΜͱΠϯετʔϧ Ͱ͖ͯɺಈ͍͍ͯΔղফͰ͖ͨෆ҆
‣ ࣌ɺɺඵΛ࣋ͭ‣ ʮ10࣌200ඵʯͳͲɺಛఆͷ࣌ࠁΛද͢‣ ͱ߹ΘͤΔ͜ͱͰɺಛఆͷ࣌ؒΛࢦ࣌͢ܭΦϒδΣΫτͷ༷Λߟ͑Δ
‣ Ϩου৽͍͠ίʔυΛॻ͘લʹɺ·ࣦͣഊ͢ΔϢχοτςετΛॻ͘ɻ৽͍͠ίʔυͷҙਤΛςετͰࣔ͢ΜͩɻϨουઃܭʹ͍͔ͭͯͬ͠Γߟ͑Δग़ൃͩɻΞδϟΠϧαϜϥΠୈষςετۦಈ։ൃ
‣ ʮ࣌ܭΫϥεʯͷΫϥε໊Ͳ͏͠Α͏?‣ Tokei ..?‣ Clock ?‣ etc..࣍ͷඪΛߟ͑Δ
```describe Clock dospecify { expect(Clock.new).to be_an_instance_of(Clock) }end```$ bundle exec rspec specbundler: failed to load command: rspec (/opt/brew/opt/rbenv/versions/2.3.0/bin/rspec)NameError: uninitialized constant ClockClockΫϥεΛ࡞Γ͍ͨ → ·ͩͳ͍͔ΒΤϥʔʹͳΔͣ
‣ ະॳظԽͷఆClockΛࢀরͨ͠Αɺͱ͍͏Τϥʔ‣ → ·ͩͳ͍͔ΒΤϥʔʹͳΔͣ ظ௨Γͷཧ༝ͰͷςετࣦഊClockΫϥεΛ࡞Δ४උ
‣ ςετ͕ظ௨Γʹࣦഊ͢Δ͜ͱΛ֬ೝ͢Δͷͱͯେࣄɻ‣ ݟͣΕͷͰΉ͜ͱ͕͋Δɻ‣ eg: SyntaxErrorΛݟམͱͨ͠··ClockΫϥεΛ ࣮ͯ͠దͳϑΟʔυόοΫʹͳΒͳ͍ظ௨Γͷཧ༝Ͱͷςετͷࣦഊ
$ mkdir lib$ vim lib/clock.rb```class Clockend```$ bundle exec rspec specbundler: failed to load command: rspec (/opt/brew/opt/rbenv/versions/2.3.0/bin/rspec)NameError: uninitialized constant ClockClockΫϥεΛ࡞Δ → ಡΈࠐ·ͳ͍ͱࢀরͰ͖ͳ͍ͷมΘΒͳ͍ͣ
‣ҙਤͱ߹͍ͬͯΔ?‣`require`͍ͯ͠ͳ͍͔ΒࢀরͰ͖ͳ͍‣→ okΆ͍ςετࣦഊͷཧ༝Λ֬ೝ͢Δ
```require 'clock'describe Clock dospecify { expect(Clock.new).to be_an_instance_of(Clock) }end```$ bundle exec rspec spec.Finished in 0.00462 seconds (files took 0.09435 seconds to load)1 example, 0 failuresrequireͨ͠ˠ͜ΜͲͦ͜௨Δͣ
‣ ϓϩμΫτίʔυͱςετίʔυΛɺͦΕͧΕ Ͳ͜ʹॻ͚Α͍ͷ͔͕Θ͔ͬͨ‣ ϓϩμΫτίʔυ lib/clock.rb‣ ςετίʔυ spec/clock_spec.rbղফͰ͖ͨෆ҆
ςετʹಋ͔Ε࣮ͯ͢ΔɺͨΊʹ༷Λཧղ͢Δ
࣌ɺɺඵΛ࣋ͭ
‣ ࣌ɺɺඵΛ༩͑ͯΠϯελϯε࡞Δ͜ͱ͕Ͱ͖Δ‣ Πϯελϯεੜ࣌ʹ༩͑ͨ࣌ɺɺඵΛɺ ͦΕͧΕऔಘͰ͖Δ‣ ࣌ܭͱͯ͠ෆదͳύϥϝʔλΛ༩͑ͨ߹ʹɺΤϥʔͱ͢Δ͜ͱ࣌ɺɺඵΛ࣋ͭ
‣ 10࣌200ඵͱ͍͏ύϥϝʔλΛ༩͑ͯɺClockΫϥεͷ ΠϯελϯεΛੜͰ͖Δɻ‣ ੜͨ͠Πϯελϯεͷʮ࣌ʯ10Ͱ͋ΔɻΠϯελϯεΛͭͬͯ͘ɺ༩͑ͨ࣌ඵΛऔಘͰ͖Δ
‣ 10࣌200ඵͱ͍͏ύϥϝʔλΛ༩͑ͯɺClockΫϥεͷΠϯελϯεΛੜͰ͖Δ‣ ίϯετϥΫλͰɺ࣌ɺɺඵΛ༩͑ΕΑ͍?‣ ੜͨ͠Πϯελϯεͷʮ࣌ʯ10Ͱ͋Δ‣ ήολʔ͕͋ΕΑͦ͞͏‣ ηολʔ? ݟ͍͑ͯΔݶΓෆཁͦ͏࣍ͷඪΛߟ͑Δ
--- a/spec/clock_spec.rb+++ b/spec/clock_spec.rb@@ -2,4 +2,10 @@ require 'clock'describe Clock dospecify { expect(Clock).to be }++ context 'new(10, 20, 00)' do+ let(:clock) { Clock.new(10, 20, 0) }++ specify { expect(clock.hour).to eq(10) }+ endendΠϯελϯεΛੜ͢Δςετ
$ bundle exec rspec.FFailures:1) Clock new(10, 20, 00)Failure/Error: let(:clock) { Clock.new(10, 20, 0) }ArgumentError:wrong number of arguments (given 3, expected 0)# ./spec/clock_spec.rb:7:in `initialize'# ./spec/clock_spec.rb:7:in `new'# ./spec/clock_spec.rb:7:in `block (3 levels) in '# ./spec/clock_spec.rb:9:in `block (3 levels) in 'Finished in 0.00186 seconds (files took 0.08364 seconds to load)2 examples, 1 failureFailed examples:rspec ./spec/clock_spec.rb:9 # Clock new(10, 20, 00)ੜ͢ΔςετˠίϯετϥΫλҾ͕ҧࣦͬͯഊ͢Δͣ
‣ άϦʔϯͱʹ͔͘ςετʹޭ͢ΔίʔυΛॻ͘ɻ࣮ͷ࠷ऴܗΛࢥ͍ඳ͚ΔͳΒҰؾʹ্͛ͯ͠·͍͍͑ɻͦ͜·Ͱͷࣗ৴͕ͳ͚Εɺ·ͣςετʹޭ͢Δఔͷ࣮͚ͩͰߏΘͳ͍ɻΞδϟΠϧαϜϥΠୈষςετۦಈ։ൃ
Ծ࣮ (Fake it)
```--- a/lib/clock.rb+++ b/lib/clock.rb@@ -1,2 +1,8 @@class Clock+ def initialize(*args)+ end++ def hour+ 10+ endend```Ծ࣮ (Fake it)
‣ ΦϒδΣΫτͷར༻ऀͱͯ͠ظ͢Δ ৼΔ͍ΛɺςετίʔυͰදݱͰ͖ͨ‣ ҙਤͨ͠ͱ͓ΓʹΤϥʔʹͳͬͨ = ςετίʔυؒҧ͍ͬͯͳͦ͏‣ ଈΛฦͤ௨ͬͨ = ·͢·͢Αͦ͞͏‣ ͜ΕΛ*ͪΌΜͱ*௨ͦ͏ղফͰ͖ͨෆ҆
ࡾ֯ଌྔ (Triangulation)
--- a/spec/clock_spec.rb+++ b/spec/clock_spec.rb@@ -8,4 +8,10 @@ describe Clock dospecify { expect(clock.hour).to eq(10) }end++ context 'new(12, 30, 00)' do+ let(:clock) { Clock.new(12, 30, 0) }++ specify { expect(clock.hour).to eq(12) }+ endendࡾ֯ଌྔ (Triangulation)
--- a/lib/clock.rb+++ b/lib/clock.rb@@ -1,8 +1,9 @@class Clock- def initialize(*args)+ def initialize(hour, *)+ @hour = hourenddef hour- 10+ @hourendend12:30ͷςετΛ௨͢Α͏ͳɺ࠷ݶͷ࣮
‣ ࠷ॳʹॻ͍ͨςετΛύεͤͨ͞·· ࣍ͷςετ௨͢ɻ‣ ࣗͷॻ͍ͨίʔυͱ͋Θͤͯͷ3Ͱɺ ಄ͷதͷίʔυͱɺ࣮ͷڑΛଌΔɻࡾ֯ଌྔ (Triangulation)
‣ [done] 10࣌200ඵͱ͍͏ύϥϝʔλΛ༩͑ͯɺClockΫϥεͷΠϯελϯεΛੜͰ͖Δ‣ [done] ੜͨ͠Πϯελϯεͷʮ࣌ʯ10Ͱ͋ΔղফͰ͖ͨෆ҆
‣ ϦϑΝΫλϦϯά࣮Λݟ͢ɻςετΛޭͤ͞ΔͨΊʹΒ͔᤻ͨ͠ͷʑΛ͖Ε͍ʹআ͠Α͏ɻॏෳΛऔΓআ͘ɻແବΛল͘ɻҙਤΛ͖ͬΓͤ͞Δɻग़དྷΔݶΓ໌ྎͰ͔Γ͍͢ίʔυʹ͠Α͏ɻΞδϟΠϧαϜϥΠୈষςετۦಈ։ൃ
‣ ςετ͕௨Δ··ͰϦϑΝΫλϦϯάΛߦ͏‣ ʮάϦʔϯ to άϦʔϯʯͱ͍͏ݴ͍ճ͠{ಈ࡞͢Δ && ͖Ε͍ͳ}ίʔυΛࢦͯ͠
--- a/lib/clock.rb+++ b/lib/clock.rb@@ -1,9 +1,8 @@class Clock+ attr_reader :hour+def initialize(hour, *)@hour = hourend- def hour- @hour- endend{ಈ࡞͢Δ && ͖Ε͍ͳ}ίʔυΛࢦͯ͠
--- a/spec/clock_spec.rb+++ b/spec/clock_spec.rb@@ -1,8 +1,6 @@require 'clock'describe Clock do- specify { expect(Clock.new).to be_an_instance_of(Clock) }-context 'new(10, 20, 00)' dolet(:clock) { Clock.new(10, 20, 0) }ςετίʔυϦϑΝΫλϦϯά͢Δ
‣ ςετίʔυϕλʹॻ͔͘ɺDRY-nessΛอ͔͕͔ͭٞΕ͍͕ͯͨɺ͍·ͷ਼ɺςετίʔυʮίʔυʯͳͷͰͪΌΜͱॻ͘ͱ͍͏ͷ‣ ॏෳแؚʹΑΔՁͷ͍ςετίʔυמΓࠐΜͰ͍͘‣ ͨͩמΓ͗ͯ͢ɺ֊ஈ͕ٸʹͳΓ͗͢Δͷྑ͘ͳ͍FYI: ΩϨΠͳίʔυ
‣ 10࣌200ඵͱ͍͏ύϥϝʔλΛ༩͑ͯɺClockΫϥεͷΠϯελϯεΛੜͰ͖Δ‣ ίϯετϥΫλͰɺ࣌ɺɺඵΛ༩͑ΕΑ͍?‣ ੜͨ͠Πϯελϯεͷʮ࣌ʯ10Ͱ͋Δ‣ 20Ͱ͋Δɻ‣ ඵ0Ͱ͋Δɻ࣍ͷඪΛߟ͑Δ
--- a/spec/clock_spec.rb+++ b/spec/clock_spec.rb@@ -5,6 +5,8 @@ describe Clock dolet(:clock) { Clock.new(10, 20, 0) }specify { expect(clock.hour).to eq(10) }+ specify { expect(clock.min).to eq(20) }+ specify { expect(clock.sec).to eq(0) }endcontext 'new(12, 30, 00)' doͱඵʹର͢Δςετˠ#hour, #minϝιου͕ͳ͍ͷͰࣦഊ͢Δͣ
໌നͳ࣮0CWJPVT*NQMFNFOUBUJPO
--- a/lib/clock.rb+++ b/lib/clock.rb@@ -1,8 +1,10 @@class Clock- attr_reader :hour+ attr_reader :hour, :min, :sec- def initialize(hour, *)+ def initialize(hour, min, sec)@hour = hour+ @min = min+ @sec = secendend໌നͳ࣮ (Obvious Implementation)
‣ [done] 10࣌200ඵͱ͍͏ύϥϝʔλΛ༩͑ͯɺClockΫϥεͷΠϯελϯεΛੜͰ͖Δ‣ [done] ੜͨ͠Πϯελϯεͷʮ࣌ʯ10Ͱ͋Δ‣ [done] ʮʯ20Ͱ͋Γɺʮඵʯ0Ͱ͋ΔղফͰ͖ͨෆ҆
ͱ߹ΘͤΔ͜ͱͰɺಛఆͷ࣌ؒΛࢦ͢
‣ ࣍ͷ࣌ɺલͷ࣌Λಋग़Ͱ͖Δ‣ ͖ΐ͏ͷʮ10:20ʯ͍͔ͭ‣ 2017-08-08T10:20:00+JSTͱ߹ΘͤΔ͜ͱͰɺಛఆͷ࣌ؒΛࢦ͢
--- a/spec/clock_spec.rb+++ b/spec/clock_spec.rb@@ -22,4 +22,11 @@ describe Clock do++ describe '#to_time' do+ let(:clock) { Clock.new(10, 20, 0) }+ let(:time) { clock.to_time(Date.today) }++ specify {+ expect(time.iso8601).to eq ‘2017-08-08T10:20:00+09:00'+ }+ endendAPIΛߟ͑Δ #to_time()͕ૉͦ͏ˠ#to_timeͷ࣮ͳ͍
--- a/lib/clock.rb+++ b/lib/clock.rb@@ -7,6 +7,10 @@ class Clock@sec = varify_valid_clock_input(sec, 60)end+ def to_time(date)+ Time.local(2017, 8, 8, 10, 20)+ end+privatedef varify_valid_clock_input(val, max)ճྫ: Ծ࣮
‣ ͱ߹ΘͤΔ͜ͱͰɺಛఆͷ࣌ؒΛࢦ͢‣ ຊͷɺ10:20Λදݱ͢Δ‣ 8/1ͷɺ10:20Λදݱ͢Δࡾ֯ଌྔ
‣ ࣍ͷ࣌ɺલͷ࣌Λಋग़Ͱ͖Δ‣ ͖ΐ͏ͷ 10:20 2017-08-08T10:20:00 JST‣ 8/1ͷ 10:20 2017-08-01T10:20:00 JSTࡾ֯ଌྔͷexampleΛ۩ମԽ
--- a/spec/clock_spec.rb+++ b/spec/clock_spec.rb@@ -25,8 +25,16 @@ describe Clock dodescribe '#to_time' dolet(:clock) { Clock.new(10, 20, 0) }- let(:time) { clock.to_time(Date.today) }+ context '(Date.today)' do+ let(:time) { clock.to_time(Date.today) }- specify { expect(time.iso8601).to eq '2017-08-08T10:20:00+09:00' }+ specify { expect(time.iso8601).to eq '2017-08-08T10:20:00+09:00' }+ end++ context '(2017-08-01)' do+ let(:time) { clock.to_time(Date.new(2017, 8, 1)) }++ specify { expect(time.iso8601).to eq '2017-08-08T10:20:00+09:00' }+ endendendࡾ֯ଌྔ
--- a/lib/clock.rb+++ b/lib/clock.rb@@ -8,7 +8,7 @@ class Clockenddef to_time(date)- Time.local(2017, 8, 8, 10, 20)+ Time.local(date.year, date.month, date.day, @hour, @min, @sec)endprivateࡾ֯ଌྔ
--- a/spec/clock_spec.rb+++ b/spec/clock_spec.rb@@ -26,7 +26,7 @@ describe Clock dodescribe '#to_time' dolet(:clock) { Clock.new(10, 20, 0) }context '(Date.today)' do- let(:time) { clock.to_time(Date.today) }+ let(:time) { clock.to_time(Date.new(2017, 8, 8)) }specify { expect(time.iso8601).to eq '2017-08-08T10:20:00+09:0endςετίʔυͷվળ: ֎෦ґଘΛແ͘͢
‣ 5%%ϓϩάϥϛϯάதͷઃܭஅͱϑΟʔυόοΫͷؒʹ͋ΔΪϟοϓΛೝࣝ͢Δ͜ͱͰ͋ΓɺͦͷΪϟοϓΛίϯτϩʔϧ͢Δٕ๏Ͱ͋Δɻ ςετۦಈ։ൃೖ‣ దͳϑΟʔυόοΫΛಘଓ͚ΔΔͨΊʹେࣄͳͷ‣ Repeatable: ͍࣮ͭߦͯ͠ಉ݁͡ՌʹͳΔ͜ͱ‣ Independent: ଞͷςετέʔεʹґଘ͠ͳ͍͜ͱΑ͍ςετίʔυͱ
‣ Q1. https://ghe.example.com/moro/emoji_renderer Λ ࣗͷϦϙδτϦͱͯ͠ fork ͠ɺͦΕΛclone͠ͳ͍͞‣ Q2. `17day` ϒϥϯνΛ࡞͠ͳ͍࣮͞श
‣ ͜͜·Ͱͷ࣭͕͋Ε࣮श͠ͳ͕ΒखΛ ڍ͍͛ͯͩ͘͞!࣭ٙλΠϜ
‣ Q3. `hello :innocent:` Λ `hello `ʹม͢Δ ϓϩάϥϜΛTDDͰ࣮͠ͳ͍͞ɻ‣ 3-1. ΠϯλʔϑΣʔεΛఆٛ͢ΔͷΈͷࣦഊ͢ΔςετΛ࡞͠ɺίϛοτ͠ͳ͍͞‣ 3-2. 3-1ͷςετ͕௨ΔΑ͏ͳԾ࣮ΛՃ͠ɺίϛοτ͠ͳ͍͞‣ 3-3. `hello :sunglasses:` Λ `hello ` ʹ͢Δࡾ֯ଌྔͷςετΛՃ͠ɺίϛοτ͠ͳ͍͞‣ 3-4. 3-1, 3-3྆ํͷςετ͕௨ΔΑ͏ʹ࣮͠ίϛοτ͠ͳ͍͞‣ ώϯτ: "\u{1f607}" "\u{1f60e}" ͰදࣔͰ͖·͢‣ ίϛοτϝοηʔδʹʮ3-xʯͷΑ͏ͳ൪߸Λ;͘Ί͍ͯͩ͘͞ɻ࣮श
‣ Q4. `hello :innocent: :sunglasses:` Λ `hello `ʹ ม͢ΔϓϩάϥϜΛTDDͰ࣮͠ͳ͍͞ɻ‣ 4-1a. ࣗ৴͕͋Δ߹ɺҰؾʹ໌֬ͳ࣮Λ͠ɺίϛοτ͠ͳ͍͞‣ 4-1b. ࣗ৴͕ͳ͍߹ɺࣦഊ͢ΔςετɺԾ࣮ɺࡾ֯ଌྔͷςετΛՃ͠ɺίϛοτ͠ͳ͍࣮͞श
‣ Q5. gemoji gem Λ͍ɺมίʔυΛϦϑΝΫλϦϯά͠ͳ͍͞‣ https://github.com/github/gemoji‣ 5-1. ςετίʔυͷมߋͤͣɺϓϩμΫτίʔυͷΈमਖ਼͠ͳ͍͞ɻ࣮श
‣ Q6. มίʔυΛ֦ு͠ɺ֦ுUnicodeͰͳ͘ λάΛग़ྗ͢Δ͜ͱʮʯՄೳʹ͠ͳ͍͞ɻ‣ 期待する出力 "hello "‣ 6-1. ग़ྗΛΓସ͑ΔΠϯλʔϑΣʔεΛઃܭ͠ɺࣦഊ͢ΔςετΛՃ͠ɺίϛοτ͠ͳ͍͞ɻ‣ 6-2. ϓϩμΫτίʔυΛ࣮͠ɺίϛοτ͠ͳ͍͞ɻ࣮श
‣ Qn. ୡͰ͖ͨίʔυΛforkͨࣗ͠ϦϙδτϦʹpush͠ɺ moro/emoji_renderer PRΛૹΓͳ͍͞‣ descriptionʹͨ͠ײΛॻ͍͍࣮ͯͩ͘͞श
͓ർΕ͞·Ͱͨ͠