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

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

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

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

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

    View Slide

  2. Здравейте, аз съм Стефан
    Saturday, August 11, 12

    View Slide


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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  10. Saturday, August 11, 12

    View Slide

  11. Saturday, August 11, 12

    View Slide

  12. Saturday, August 11, 12

    View Slide

  13. Saturday, August 11, 12

    View Slide

  14. Saturday, August 11, 12

    View Slide

  15. The Real Stuff
    Saturday, August 11, 12

    View Slide

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

    View Slide

  17. Saturday, August 11, 12

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  24. Тестовете правят
    промяната на
    приложението по-лесна.
    Saturday, August 11, 12

    View Slide

  25. Saturday, August 11, 12

    View Slide

  26. Saturday, August 11, 12

    View Slide

  27. Saturday, August 11, 12

    View Slide

  28. Saturday, August 11, 12

    View Slide

  29. Safety Net
    Saturday, August 11, 12

    View Slide

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

    View Slide

  31. Defect Localization
    Saturday, August 11, 12

    View Slide

  32. Bug Repellent
    Saturday, August 11, 12

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  36. Трудна истина: трябваше
    вече да сте се научили
    Saturday, August 11, 12

    View Slide

  37. “If you don’t write tests,
    you need to finish learning
    how to write code.”
    Saturday, August 11, 12

    View Slide

  38. Saturday, August 11, 12

    View Slide

  39. Навлизането в света на
    автоматизираното
    тестване по никакъв начин
    не е лесно и отнема време
    Saturday, August 11, 12

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  48. System Under Test (SUT)
    друго име за “какво точно тестваме”
    ще го произнасям “СУТ”
    всеки тест трябва да има ясна такава
    помага да категоризираме тестовете
    Saturday, August 11, 12

    View Slide

  49. SUT
    Tests
    Unit tests Integration
    tests
    Saturday, August 11, 12

    View Slide

  50. Unit Integration
    Тества един клас
    Изолиран с mock-ове
    Бърз
    Помага за дизайна
    Тества няколко неща
    Понякога има stub-ове
    Бавен
    Помага за верификацията
    Saturday, August 11, 12

    View Slide

  51. Registration
    Invite
    Mailer
    UserRecord
    Example Design
    Saturday, August 11, 12

    View Slide

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

    View Slide

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

    View Slide

  54. Registration
    Invite
    Mailer
    UserRecord
    Unit Test (w/ mocks)
    Mock
    Mock
    Saturday, August 11, 12

    View Slide

  55. class Stack < Test::Unit::TestCase
    def setup
    @stack = Stack.new
    end
    def test_empty
    assert @stack.empty?
    @stack.push 42
    assert [email protected]?
    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

    View Slide

  56. class RegistrationTest < Test::Unit::TestCase
    def test_registering_a_valid_user
    registration = Registration.new
    registration.email = '[email protected]'
    registration.password = 'larodi'
    registration.submit
    assert_equal 1, User.count
    assert_equal '[email protected]', User.first.email
    end
    def test_user_without_an_email
    registration = Registration.new
    registration.email = ''
    # ...
    end
    end
    Integration
    Saturday, August 11, 12

    View Slide

  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

    View Slide

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

    View Slide

  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

    View Slide

  60. Важно е за ∀ test case да
    е ясно къкъв SUT има
    Saturday, August 11, 12

    View Slide

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

    View Slide

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

    View Slide

  63. Warning: mock-овете
    са висш пилотаж
    Saturday, August 11, 12

    View Slide

  64. Saturday, August 11, 12

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  68. example: controller specs
    Saturday, August 11, 12

    View Slide

  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

    View Slide

  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

    View Slide

  71. Изглежда, всякаш не тествате
    нищо съществено. Въпреки
    това, има предимства.
    Saturday, August 11, 12

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  76. объркващи понятия
    Saturday, August 11, 12

    View Slide

  77. Saturday, August 11, 12

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  93. 1 2 3
    TDD =
    Saturday, August 11, 12

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  97. Disclaimer
    Saturday, August 11, 12

    View Slide

  98. 1 2 3
    Кодът, който искате да
    имате
    describe "Message" do
    it "should support initialization" do
    message = Message.new('[email protected]', '[email protected]', 'Hi!')
    message.from.should == '[email protected]'
    message.to.should == '[email protected]'
    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

    View Slide

  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

    View Slide

  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

    View Slide

  101. 1 2 3
    Изразявате новата функционалност в
    тест
    describe 'Message' do
    # ...
    it "should validate 'from'" do
    # bacon.should be_valid 㱻 assert bacon.valid?
    Message.new('[email protected]', '[email protected]', 'Hi!').should be_valid
    Message.new('foo.bg', '[email protected]', 'Hi!').should_not be_valid
    Message.new('[email protected]', '[email protected]', 'Hi!').should_not be_valid
    end
    end
    .F
    1)
    NoMethodError in 'Message should validate 'from''
    undefined method `valid?' for #
    /work/message/spec/message_spec.rb:13:
    Finished in 0.010847 seconds
    2 examples, 1 failure
    Saturday, August 11, 12

    View Slide

  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][email protected][a-z]+(\.[a-z]+)+$/
    end
    end
    ..
    Finished in 0.011689 seconds
    2 examples, 0 failures
    Saturday, August 11, 12

    View Slide

  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][email protected][a-z]+(\.[a-z]+)+$/
    end
    end
    Все още всичко е ОК
    Saturday, August 11, 12

    View Slide

  104. 1 2 3
    Отново, почвате с тест преди кода
    describe 'Message' do
    # ...
    it "should validate 'to'" do
    Message.new('[email protected]', '[email protected]', 'Hi!').should be_valid
    Message.new('[email protected]', 'bender', 'Hi!').should_not be_valid
    Message.new('[email protected]', '[email protected]', '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

    View Slide

  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][email protected][a-z]+(\.[a-z]+)+$/ and
    @to =~ /^[a-z][email protected][a-z]+(\.[a-z]+)+$/
    end
    end
    ...
    Finished in 0.010058 seconds
    3 examples, 0 failures
    Saturday, August 11, 12

    View Slide

  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][email protected][a-z]+(\.[a-z]+)+$/ and
    @to =~ /^[a-z][email protected][a-z]+(\.[a-z]+)+$/
    end
    end
    1 2 3
    Повторение
    Saturday, August 11, 12

    View Slide

  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][email protected][a-z]+(\.[a-z]+)+$/ and
    @to =~ /^[a-z][email protected][a-z]+(\.[a-z]+)+$/
    end
    private
    def email_valid?(address)
    address =~ /^[a-z][email protected][a-z]+(\.[a-z]+)+$/
    end
    end
    1 2 3
    Малки стъпки
    ...
    Finished in 0.010158 seconds
    3 examples, 0 failures
    Saturday, August 11, 12

    View Slide

  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][email protected][a-z]+(\.[a-z]+)+$/
    end
    private
    def email_valid?(address)
    address =~ /^[a-z][email protected][a-z]+(\.[a-z]+)+$/
    end
    end
    1 2 3
    Ама наистина малки
    стъпки
    ...
    Finished in 0.010001 seconds
    3 examples, 0 failures
    Saturday, August 11, 12

    View Slide

  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][email protected][a-z]+(\.[a-z]+)+$/
    end
    end
    1 2 3
    Готово
    ...
    Finished in 0.009903 seconds
    3 examples, 0 failures
    Saturday, August 11, 12

    View Slide

  110. Saturday, August 11, 12

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  115. Saturday, August 11, 12

    View Slide

  116. false positive
    Saturday, August 11, 12

    View Slide

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

    View Slide

  118. sans debugging
    Saturday, August 11, 12

    View Slide

  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

    View Slide

  120. Намалява нуждата от
    debugging почти напълно,
    понеже работите на малки
    стъпки и знаете кога сте
    въвели бъга
    Saturday, August 11, 12

    View Slide

  121. Scope creep
    Saturday, August 11, 12

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  125. Няма време!
    Saturday, August 11, 12

    View Slide

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

    View Slide

  127. Saturday, August 11, 12

    View Slide

  128. Saturday, August 11, 12

    View Slide

  129. Saturday, August 11, 12

    View Slide

  130. Saturday, August 11, 12

    View Slide

  131. Saturday, August 11, 12

    View Slide

  132. fin
    Saturday, August 11, 12

    View Slide

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

    View Slide