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

Unit Test-ове, TDD и всичко останало

Unit Test-ове, TDD и всичко останало

Презентация за тестове от VarnaConf

5ca07e641fada5a88a09277c45bd7c1b?s=128

Stefan Kanev

August 11, 2012
Tweet

More Decks by Stefan Kanev

Other Decks in Programming

Transcript

  1. Unit Test-ове, TDD и всичко останало... Стефан Кънев http://skanev.com/ @skanev

    VarnaConf 11 август 2012 София Saturday, August 11, 12
  2. Здравейте, аз съм Стефан Saturday, August 11, 12

  3. ❤ тестове Saturday, August 11, 12

  4. преди това: нещо за мен Saturday, August 11, 12

  5. Всеки път Saturday, August 11, 12

  6. “Разчупен лед” Saturday, August 11, 12

  7. обикновено измислям нещо дълго днес нямам време Saturday, August 11,

    12
  8. 3. 14159265358979323846264338327950288419716939937510 58209749445923078164062862089986280348253421170679 82148086513282306647093844609550582231725359408128 48111745028410270193852110555964462294895493038196 44288109756659334461284756482337867831652712019091 45648566923460348610454326648213393607260249141273 72458700660631558817488152092096282925409171536436 78925903600113305305488204665213841469519415116094 33057270365759591953092186117381932611793105118548

    07446237996274956735188575272489122793818301194912 98336733624406566430860213949463952247371907021798 60943702770539217176293176752384674818467669405132 00056812714526356082778577134275778960917363717872 14684409012249534301465495853710507922796892589235 42019956112129021960864034418159813629774771309960 51870721134999999837297804995105973173281609631859 50244594553469083026425223082533446850352619311881 71010003137838752886587533208381420617177669147303 59825349042875546873115956286388235378759375195778 18577805321712268066130019278766111959092164201989 38095257201065485863278865936153381827968230301952 03530185296899577362259941389124972177528347913151 55748572424541506959508295331168617278558890750983 81754637464939319255060400927701671139009848824012 85836160356370766010471018194295559619894676783744 94482553797747268471040475346462080466842590694912 93313677028989152104752162056966024058038150193511 25338243003558764024749647326391419927260426992279 67823547816360093417216412199245863150302861829745 55706749838505494588586926995690927210797509302955 Saturday, August 11, 12
  9. instance Show Board where show (Board ps) = let ordered

    = (sort . swap) ps ranks = map (showRank ordered) [8,7..1] board = intersperse "--+--+--+--+--+--+--+--" ranks rlabels = intersperse " " (map (\n->(show n)++" ") [8,7..1]) flabels = " a b c d e f g h" in unlines $ zipWith (++) rlabels board ++ [flabels] where swap = map (\(a,b)->(b,a)) showRank ps r = let rnk = filter (\(p,_)->(rank p)==r) ps cs = map (showPiece rnk) [A .. H] in concat (intersperse "|" cs) showPiece ps f = maybe " " (show . snd) (find (\(p,_)->(file p)==f) ps) Saturday, August 11, 12
  10. Saturday, August 11, 12

  11. Saturday, August 11, 12

  12. Saturday, August 11, 12

  13. Saturday, August 11, 12

  14. Saturday, August 11, 12

  15. The Real Stuff Saturday, August 11, 12

  16. Готин тип! Просяк? @skanev @krokodilerian Saturday, August 11, 12

  17. Saturday, August 11, 12

  18. сега по-сериозно Saturday, August 11, 12

  19. Сухи факти Saturday, August 11, 12

  20. Цел: да виждате голямата картина (макар и размазано) Saturday, August

    11, 12
  21. Средства: няколко привидно несвързани неща, касаещи тестването Saturday, August 11,

    12
  22. 1. Защо пишем тестове? 2. Как да се научим? 3.

    Unit vs. Integration тестове 4. Mocks 5. Test-Driven Development 6. Често срещани проблеми План Saturday, August 11, 12
  23. 1. Защо пишем тестове? 2. Как да се научим? 3.

    Unit vs. Integration тестове 4. Mocks 5. Test-Driven Development 6. Често срещани проблеми План Saturday, August 11, 12
  24. Тестовете правят промяната на приложението по-лесна. Saturday, August 11, 12

  25. Saturday, August 11, 12

  26. Saturday, August 11, 12

  27. Saturday, August 11, 12

  28. Saturday, August 11, 12

  29. Safety Net Saturday, August 11, 12

  30. Документация Saturday, August 11, 12

  31. Defect Localization Saturday, August 11, 12

  32. Bug Repellent Saturday, August 11, 12

  33. Тестовете улесняват поддръжката на проекта. Saturday, August 11, 12

  34. Лошо написаните тестове правят системата по-трудна за поддръжка Saturday, August

    11, 12
  35. 1. Защо пишем тестове? 2. Как да се научим? 3.

    Unit vs. Integration тестове 4. Mocks 5. Test-Driven Development 6. Често срещани проблеми План Saturday, August 11, 12
  36. Трудна истина: трябваше вече да сте се научили Saturday, August

    11, 12
  37. “If you don’t write tests, you need to finish learning

    how to write code.” Saturday, August 11, 12
  38. Saturday, August 11, 12

  39. Навлизането в света на автоматизираното тестване по никакъв начин не

    е лесно и отнема време Saturday, August 11, 12
  40. Има много грешки, които може да направите (за тях по-късно)

    Saturday, August 11, 12
  41. червено жълто зелено синьо червено синьо жълто зелено синьо Saturday,

    August 11, 12
  42. Тестовете също са код. Принципите за качествен код важат и

    за тях Saturday, August 11, 12
  43. (1) практикуване (2) рефлектиране (3) четене Saturday, August 11, 12

  44. 1. Практика Saturday, August 11, 12

  45. 2. Рефлектиране Saturday, August 11, 12

  46. 3. Четене Saturday, August 11, 12

  47. 1. Защо пишем тестове? 2. Как да се научим? 3.

    Unit vs. Integration тестове 4. Mocks 5. Test-Driven Development 6. Често срещани проблеми План Saturday, August 11, 12
  48. System Under Test (SUT) друго име за “какво точно тестваме”

    ще го произнасям “СУТ” всеки тест трябва да има ясна такава помага да категоризираме тестовете Saturday, August 11, 12
  49. SUT Tests Unit tests Integration tests Saturday, August 11, 12

  50. Unit Integration Тества един клас Изолиран с mock-ове Бърз Помага

    за дизайна Тества няколко неща Понякога има stub-ове Бавен Помага за верификацията Saturday, August 11, 12
  51. Registration Invite Mailer UserRecord Example Design Saturday, August 11, 12

  52. Registration Invite Mailer UserRecord Unit Test Saturday, August 11, 12

  53. Registration Invite Mailer UserRecord Integration Test Saturday, August 11, 12

  54. Registration Invite Mailer UserRecord Unit Test (w/ mocks) Mock Mock

    Saturday, August 11, 12
  55. class Stack < Test::Unit::TestCase def setup @stack = Stack.new end

    def test_empty assert @stack.empty? @stack.push 42 assert !@stack.empty? end def test_pop @stack.push 42 @stack.push 5 assert_equal 5, @stack.pop assert_equal 42, @stack.pop asser_raise(StackEmptyError) { @stack.pop } end end Unit Saturday, August 11, 12
  56. class RegistrationTest < Test::Unit::TestCase def test_registering_a_valid_user registration = Registration.new registration.email

    = 'stefan@example.org' registration.password = 'larodi' registration.submit assert_equal 1, User.count assert_equal 'stefan@example.org', User.first.email end def test_user_without_an_email registration = Registration.new registration.email = '' # ... end end Integration Saturday, August 11, 12
  57. Feature: Purchasing things In order for us to make money

    the user should be allowed to easily purchase our products Scenario: Purchases for more than 2500 should offer 20% Given the following products: | Name | Price | | MacBook Pro | 2000 | | ThinkPad | 1000 | When I add 1 "MacBook Pro" to my basket And I add 1 "ThinkPad" to my basket Then the final price should be 2600 Integration Saturday, August 11, 12
  58. Функционалност: Купуване на продукти За да печелим пари потребителят трябва

    да може лесно да пазарува продукти Сценарий: Поръчките за повече от 2000 трябва да имат 20% отстъпка Дадено че има следните три продукта: | Име | Price | | MacBook Air | 2000 | | ThinkPad | 1000 | Когато добавя 1 "MacBook Air" в кошницата си И добавя 2 "ThinkPad" в кошницата си То цената трябва да е 2600 Integration Saturday, August 11, 12
  59. describe RegistrationsController do describe "PUT update" do context "when valid"

    do let(:registration) { double } before do Registration.stub :new => registration registration.stub :create => true end it "constructs a Registration with params[:registration]" do Registration.should_receive(:new).with('registration data') post :create, registration: 'registration data' end it "creates a registration" do registration.should_receive(:create) post :create end end end end Unit Saturday, August 11, 12
  60. Важно е за ∀ test case да е ясно къкъв

    SUT има Saturday, August 11, 12
  61. Трябва ви комбинация от unit и integration тестове Saturday, August

    11, 12
  62. 1. Защо пишем тестове? 2. Как да се научим? 3.

    Unit vs. Integration тестове 4. Mocks 5. Test-Driven Development 6. Често срещани проблеми План Saturday, August 11, 12
  63. Warning: mock-овете са висш пилотаж Saturday, August 11, 12

  64. Saturday, August 11, 12

  65. Stubs Mocks Spies Fakes Test Doubles Saturday, August 11, 12

  66. Mock-овете не имплементират поведение, а поставят очаквания към кода Saturday,

    August 11, 12
  67. • Този метод трябва да се извика с тези параметри

    • Този метод не трябва да се извиква Тестовете с mock-ове често поставят очаквания като: Saturday, August 11, 12
  68. example: controller specs Saturday, August 11, 12

  69. class RegistrationsController < ApplicationController def create @registration = Registration.new params[:registration]

    if @registration.create redirect_to root_path else render :action => :new end end end Saturday, August 11, 12
  70. describe RegistrationsController do describe "PUT update" do context "when valid"

    do let(:registration) { double } before do Registration.stub :new => registration registration.stub :create => true end it "constructs a Registration with params[:registration]" do Registration.should_receive(:new).with('registration data') post :create, registration: 'registration data' end it "creates a registration" do registration.should_receive(:create) post :create end end end end Saturday, August 11, 12
  71. Изглежда, всякаш не тествате нищо съществено. Въпреки това, има предимства.

    Saturday, August 11, 12
  72. Mock-овете държат интерфейса между контролер и модел тесен Saturday, August

    11, 12
  73. Тестовете на контролерите са по-бързи и по-точни Saturday, August 11,

    12
  74. Когато пишете теста на контролера, преди да имплементирате модела, правите

    interface discovery Saturday, August 11, 12
  75. 1. Защо пишем тестове? 2. Как да се научим? 3.

    Unit vs. Integration тестове 4. Mocks 5. Test-Driven Development 6. Често срещани проблеми План Saturday, August 11, 12
  76. объркващи понятия Saturday, August 11, 12

  77. Saturday, August 11, 12

  78. Моят браузър е Гугъл, бабиното. Saturday, August 11, 12

  79. Правя Test-Driven Development. Saturday, August 11, 12

  80. Правя Test-Driven Development. Пиша тестове. Saturday, August 11, 12

  81. test-driven development automated testing Saturday, August 11, 12

  82. Инструмент Автоматизирани тестове Saturday, August 11, 12

  83. Подход Test-Driven Development Test-Driven Development Saturday, August 11, 12

  84. “Test-Driven Development is not about testing” — Dan North Saturday,

    August 11, 12
  85. дизайн Saturday, August 11, 12

  86. TDD = Test-Driven Design Saturday, August 11, 12

  87. на практика Saturday, August 11, 12

  88. Добавяте тест 1 ...за несъществуващ код Пишете код 2 ...колкото

    тестът да мине Правите подобрения 3 ...докато премахнете повторенията Saturday, August 11, 12
  89. Добавяте тест 1 ...за несъществуващ код Пишете код 2 ...колкото

    тестът да мине Правите подобрения 3 ...докато премахнете повторенията Saturday, August 11, 12
  90. Добавяте тест 1 ...за несъществуващ код Пишете код 2 ...колкото

    тестът да мине Правите подобрения 3 ...докато премахнете повторенията • Тествате кода, който бихте искали да имате • Няма да се компилира (липсващи методи/класове) • Пускате го и гледате как се проваля • Имате червен тест проверяващ функционалността Saturday, August 11, 12
  91. Добавяте тест 1 ...за несъществуващ код Пишете код 2 ...колкото

    тестът да мине Правите подобрения 3 ...докато премахнете повторенията • Добавяте достатъчно код за да мине теста • Нито ред повече • Най-простото решение, което ви хрумва • Имате работещ код и зелен тест, който го потвърждава Saturday, August 11, 12
  92. Добавяте тест 1 ...за несъществуващ код Пишете код 2 ...колкото

    тестът да мине Правите подобрения 3 ...докато премахнете повторенията • Не добавяте функционалност • Подобрявате кода/дизайна • Премахвате повторенията • На всяка стъпка пускате теста Saturday, August 11, 12
  93. 1 2 3 TDD = Saturday, August 11, 12

  94. четвъртата стъпка Saturday, August 11, 12

  95. Прочитате съобщението 1.5 ...и се уверявате, че е достатъчно информативно

    Saturday, August 11, 12
  96. пример Saturday, August 11, 12

  97. Disclaimer Saturday, August 11, 12

  98. 1 2 3 Кодът, който искате да имате describe "Message"

    do it "should support initialization" do message = Message.new('fry@foo.bg', 'bender@foo.bg', 'Hi!') message.from.should == 'fry@foo.bg' message.to.should == 'bender@foo.bg' message.title.should == 'Hi!' end end F 1) NameError in 'Message should support initialization' uninitialized constant Message /work/message/spec/message_spec.rb:5: Finished in 0.009336 seconds 1 example, 1 failure Saturday, August 11, 12
  99. 1 2 3 Най-простата имплементация class Message attr_reader :from, :to,

    :title def initialize(from, to, title) @from = from @to = to @title = title end end . Finished in 0.009999 seconds 1 example, 0 failures Saturday, August 11, 12
  100. 1 2 3 Пас class Message attr_reader :from, :to, :title

    def initialize(from, to, title) @from = from @to = to @title = title end end Всичко изглежда ок, няма нужда от рефакториране Saturday, August 11, 12
  101. 1 2 3 Изразявате новата функционалност в тест describe 'Message'

    do # ... it "should validate 'from'" do # bacon.should be_valid 㱻 assert bacon.valid? Message.new('fry@foo.bg', 'bender@foo.bg', 'Hi!').should be_valid Message.new('foo.bg', 'bender@foo.bg', 'Hi!').should_not be_valid Message.new('fry@foo', 'bender@foo.bg', 'Hi!').should_not be_valid end end .F 1) NoMethodError in 'Message should validate 'from'' undefined method `valid?' for #<Message:0x100327e08> /work/message/spec/message_spec.rb:13: Finished in 0.010847 seconds 2 examples, 1 failure Saturday, August 11, 12
  102. 1 2 3 Прост регулярен израз class Message attr_reader :from,

    :to, :title def initialize(from, to, title) @from = from @to = to @title = title end def valid? @from =~ /^[a-z]+@[a-z]+(\.[a-z]+)+$/ end end .. Finished in 0.011689 seconds 2 examples, 0 failures Saturday, August 11, 12
  103. 1 2 3 Отново пас class Message attr_reader :from, :to,

    :title def initialize(from, to, title) @from = from @to = to @title = title end def valid? @from =~ /^[a-z]+@[a-z]+(\.[a-z]+)+$/ end end Все още всичко е ОК Saturday, August 11, 12
  104. 1 2 3 Отново, почвате с тест преди кода describe

    'Message' do # ... it "should validate 'to'" do Message.new('fry@foo.bg', 'bender@foo.bg', 'Hi!').should be_valid Message.new('fry@foo.bg', 'bender', 'Hi!').should_not be_valid Message.new('fry@foo.bg', 'bender@foo', 'Hi!').should_not be_valid end end ..F 1) 'Message should validate 'to'' FAILED expected valid? to return false, got 0 /work/message/spec/message_spec.rb:20: Finished in 0.009825 seconds 3 examples, 1 failure Saturday, August 11, 12
  105. 1 2 3 Най-простата имплементация class Message attr_reader :from, :to,

    :title def initialize(from, to, title) @from = from @to = to @title = title end def valid? @from =~ /^[a-z]+@[a-z]+(\.[a-z]+)+$/ and @to =~ /^[a-z]+@[a-z]+(\.[a-z]+)+$/ end end ... Finished in 0.010058 seconds 3 examples, 0 failures Saturday, August 11, 12
  106. class Message attr_reader :from, :to, :title def initialize(from, to, title)

    @from = from @to = to @title = title end def valid? @from =~ /^[a-z]+@[a-z]+(\.[a-z]+)+$/ and @to =~ /^[a-z]+@[a-z]+(\.[a-z]+)+$/ end end 1 2 3 Повторение Saturday, August 11, 12
  107. class Message attr_reader :from, :to, :title def initialize(from, to, title)

    @from = from @to = to @title = title end def valid? @from =~ /^[a-z]+@[a-z]+(\.[a-z]+)+$/ and @to =~ /^[a-z]+@[a-z]+(\.[a-z]+)+$/ end private def email_valid?(address) address =~ /^[a-z]+@[a-z]+(\.[a-z]+)+$/ end end 1 2 3 Малки стъпки ... Finished in 0.010158 seconds 3 examples, 0 failures Saturday, August 11, 12
  108. class Message attr_reader :from, :to, :title def initialize(from, to, title)

    @from = from @to = to @title = title end def valid? email_valid?(@from) and @to =~ /^[a-z]+@[a-z]+(\.[a-z]+)+$/ end private def email_valid?(address) address =~ /^[a-z]+@[a-z]+(\.[a-z]+)+$/ end end 1 2 3 Ама наистина малки стъпки ... Finished in 0.010001 seconds 3 examples, 0 failures Saturday, August 11, 12
  109. class Message attr_reader :from, :to, :title def initialize(from, to, title)

    @from = from @to = to @title = title end def valid? email_valid?(@from) and email_valid?(@to) end private def email_valid?(address) address =~ /^[a-z]+@[a-z]+(\.[a-z]+)+$/ end end 1 2 3 Готово ... Finished in 0.009903 seconds 3 examples, 0 failures Saturday, August 11, 12
  110. Saturday, August 11, 12

  111. Тестът е пример за интерфейса, който имплементирате Saturday, August 11,

    12
  112. Принуждава ви да измислите дизайн Saturday, August 11, 12

  113. Дизайнът е основан на обратна връзка, а не на спекулация

    Saturday, August 11, 12
  114. гарантира ви че имате тест Saturday, August 11, 12

  115. Saturday, August 11, 12

  116. false positive Saturday, August 11, 12

  117. гарантира, че кодът е тестваем Saturday, August 11, 12

  118. sans debugging Saturday, August 11, 12

  119. Everyone knows that debugging is twice as hard as writing

    a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it? — Brian Kenighan Saturday, August 11, 12
  120. Намалява нуждата от debugging почти напълно, понеже работите на малки

    стъпки и знаете кога сте въвели бъга Saturday, August 11, 12
  121. Scope creep Saturday, August 11, 12

  122. Y.A.G.N.I. Y! ain’t gonna need it Saturday, August 11, 12

  123. Ритъм Saturday, August 11, 12

  124. 1. Защо пишем тестове? 2. Как да се научим? 3.

    Unit vs. Integration тестове 4. Mocks 5. Test-Driven Development 6. Често срещани проблеми План Saturday, August 11, 12
  125. Няма време! Saturday, August 11, 12

  126. Книги Saturday, August 11, 12

  127. Saturday, August 11, 12

  128. Saturday, August 11, 12

  129. Saturday, August 11, 12

  130. Saturday, August 11, 12

  131. Saturday, August 11, 12

  132. fin Saturday, August 11, 12

  133. Въпроси Saturday, August 11, 12