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

Testing Ruby

Testing Ruby

Slides for my presentation at HackBulgaria Ruby course - coreruby.github.io

Radoslav Stankov

December 04, 2014
Tweet

More Decks by Radoslav Stankov

Other Decks in Technology

Transcript

  1. Testing Ruby
    Radoslav Stankov 04/12/2014

    View Slide

  2. Кой съм аз?
    @rstankov

    http://rstankov.com

    http://github.com/rstankov

    View Slide

  3. View Slide

  4. • 2002 - първия ми “професионален проект” (flash)
    • 2008 - първи PHP unit test
    • 2009 - първи Ruby on Rails проект
    • 2009 - първи Ruby unit test
    • 2010 - официално Ruby developer
    • 2010 - първи selenium acceptance test
    • 2014 - все още пиша главно на Ruby … :P

    View Slide

  5. View Slide

  6. I have a plan!

    View Slide

  7. • За какво са ни нужни тестове?
    • Какво е автоматичен тест?
    • Какви видове тестове има?
    • Какво е добър тест?
    • Как да пишем добри тестове?
    • Какво трябва да тестваме?
    • Какво са “Mocks”?
    • Какво е “RSpec”?
    • Какво е “Test Driven Development”?
    • Какво е “Fragile tests”?

    View Slide

  8. За какво са ни нужни
    тестове?

    View Slide

  9. View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. View Slide

  14. View Slide

  15. View Slide

  16. puts "1 + 1 = #{Calculation.new("1 + 1").value}"
    puts "2 - 1 = #{Calculation.new("2 - 1").value}"
    puts "3 * 2 = #{Calculation.new("3 * 2").value}"

    View Slide

  17. class Calculation
    # ...
    def to_s
    "#{expression} = #{value}"
    end
    end

    View Slide

  18. puts Calculation.new("1 + 1")
    puts Calculation.new("2 - 1")
    puts Calculation.new("3 * 2")

    View Slide

  19. raise "INVALID" unless Calculation.new("1 + 1").value == 2
    raise "INVALID" unless Calculation.new("2 - 1").value == 1
    raise "INVALID" unless Calculation.new("3 * 2").value == 6
    puts 'Everything okay :)'

    View Slide

  20. def assert_calculation(expression, expected)
    calculation = Calculation.new(expression)
    if calculation.value != expected
    raise "#{calculation}, but expected #{expected}"
    end
    end

    View Slide

  21. assert_calculation '1 + 1', 2
    assert_calculation '2 - 1', 1
    assert_calculation '3 * 2', 6
    puts 'Everything okay :)'

    View Slide

  22. • code task 1
    • check task 1

    • code task 2
    • check task 2
    • check task 1

    • code task 3
    • check task 3
    • check task 2
    • check task 1

    • code task 4
    • check task 4
    • check task 3
    • check task 2
    • check task 1
    • code task 1
    • code task 1 test
    • run tests

    • code task 2
    • code task 2 test
    • run tests

    • code task 3
    • code task 3 test
    • run tests

    • code task 4
    • code task 4 test
    • run tests
    Manual Automatic

    View Slide

  23. • code task 1
    • check task 1

    • code task 2
    • check task 2
    • check task 1

    • code task 3
    • check task 3
    • check task 2
    • check task 1

    • code task 4
    • check task 4
    • check task 3
    • check task 2
    • check task 1
    • code task 5
    • check task 4
    • check task 4
    • check task 3
    • check task 2
    • code task 1
    • code task 1 test
    • run tests

    • code task 2
    • code task 2 test
    • run tests

    • code task 3
    • code task 3 test
    • run tests

    • code task 4
    • code task 4 test
    • run tests
    • code task 5
    • code task 5 test
    • run tests
    Manual Automatic

    View Slide

  24. • Safety net
    • Reduces bugs in new or existing features
    • Forces you to understand the features and
    communication between components
    • Reduces the cost for new features
    • Making code testable decreases complexity and
    modularity

    View Slide

  25. Какво е автоматичен
    тест?

    View Slide

  26. class CalculationTest < Minitest::Test
    def test_add
    assert_equal 2, Calculation.new('1 + 1').value
    end
    def test_subtract
    assert_equal 1, Calculation.new('2 - 1').value
    end
    def test_multiply
    assert_equal 6, Calculation.new('3 * 2').value
    end
    end

    View Slide

  27. View Slide

  28. class ArrayTest < Minitest::Test
    def setup
    @array = []
    end
    def test_empty
    assert @array.empty?
    end
    def test_push
    @array << 1
    assert_equal [1], @array
    end
    end

    View Slide

  29. class ArrayTest < Minitest::Test
    def setup
    @array = []
    end
    def test_empty
    assert @array.empty?
    end
    def test_push
    @array << 1
    assert_equal [1], @array
    end
    end
    Test Suite

    View Slide

  30. class ArrayTest < Minitest::Test
    def setup
    @array = []
    end
    def test_empty
    assert @array.empty?
    end
    def test_push
    @array << 1
    assert_equal [1], @array
    end
    end

    View Slide

  31. class ArrayTest < Minitest::Test
    def setup
    @array = []
    end
    def test_empty
    assert @array.empty?
    end
    def test_push
    @array << 1
    assert_equal [1], @array
    end
    end
    Test Setup

    View Slide

  32. class ArrayTest < Minitest::Test
    def setup
    @array = []
    end
    def test_empty
    assert @array.empty?
    end
    def test_push
    @array << 1
    assert_equal [1], @array
    end
    end

    View Slide

  33. class ArrayTest < Minitest::Test
    def setup
    @array = []
    end
    def test_empty
    assert @array.empty?
    end
    def test_push
    @array << 1
    assert_equal [1], @array
    end
    end
    Test Case

    View Slide

  34. class ArrayTest < Minitest::Test
    def setup
    @array = []
    end
    def test_empty
    assert @array.empty?
    end
    def test_push
    @array << 1
    assert_equal [1], @array
    end
    end

    View Slide

  35. class ArrayTest < Minitest::Test
    def setup
    @array = []
    end
    def test_empty
    assert @array.empty?
    end
    def test_push
    @array << 1
    assert_equal [1], @array
    end
    end
    Assertion

    View Slide

  36. class ArrayTest < Minitest::Test
    def setup
    @array = []
    end
    def test_empty
    assert @array.empty?
    end
    def test_push
    @array << 1
    assert_equal [1], @array
    end
    end

    View Slide

  37. Kent Beck
    • based on SUnit (Smalltalk)
    • released around 1998
    xUnit

    View Slide

  38. Какви видове тестове
    има?

    View Slide

  39. System under test (SUT)
    Tests
    Unit test Integration test Acceptance test
    SUD

    View Slide

  40. Unit Test Integration Test Acceptance Test
    Tests one object Tests one “component” Tests the whole stack
    Isolated with mocks Some times uses stubs
    Uses the UI

    Expresses domain terms
    Fast Slow Very slow
    Helps for good design Helps for verification Helps for verification

    View Slide

  41. Какво е добър тест?

    View Slide

  42. “Не трябва да губя енергия за пускане на
    тестове ”
    Лесни за пускане

    View Slide

  43. “Пускането на тестове не трябва да ме
    разсейва от проблема, който решавам ”
    Бързи

    View Slide

  44. “Не трябва да губя излишна енегрия зада
    разбера даден тест”
    Прости

    View Slide

  45. “Трябва да имам чувството, че ако тестовете
    ми минават, значи кода ми работи”
    Надежни

    View Slide

  46. “Не трябва да се налага да се пренаписват
    при всяка промяна ”
    Гъвкави

    View Slide

  47. “Когато даден тест се счупи, трябва да знам
    точно кой тест се е счупил и защо”
    Локализирани

    View Slide

  48. Как да пишем добри
    тестове?

    View Slide

  49. View Slide

  50. 4 Phase testing

    View Slide

  51. 4 Phase testing
    Setup

    View Slide

  52. 4 Phase testing
    Setup
    Action

    View Slide

  53. 4 Phase testing
    Setup
    Action
    Assertion

    View Slide

  54. 4 Phase testing
    Setup
    Action
    Assertion
    Teardown

    View Slide

  55. 4 Phase testing
    Setup
    Action
    Assertion
    Teardown

    View Slide

  56. class OrderTest < Minitest::Test
    def test_proccess
    order = Order.new
    assert_equal Date.today, order.initiated_at.to_date
    assert_nil order.user
    assert order.empty?
    product = Product.new
    order << product
    assert_equal 1, order.size
    assert !order.valid?
    user = User.new
    order.user = user
    assert_equal user, order.user
    assert !order.valid?

    View Slide

  57. assert !order.valid?
    user = User.new
    order.user = user
    assert_equal user, order.user
    assert !order.valid?
    def Order.can_be_processed_today?
    true
    end
    assert order.valid?
    order.cancel
    assert_equal :canceled, order.state
    end
    end

    View Slide

  58. class OrderTest < Minitest::Test
    def test_proccess
    order = Order.new
    assert_equal Date.today, order.initiated_at.to_date
    assert_nil order.user
    assert order.empty?
    product = Product.new
    order << product
    assert_equal 1, order.size
    assert !order.valid?
    user = User.new
    order.user = user
    assert_equal user, order.user
    assert !order.valid?
    def Order.can_be_processed_today?
    true
    end
    assert order.valid?
    order.cancel
    assert_equal :canceled, order.state
    end
    end

    View Slide

  59. View Slide

  60. View Slide

  61. View Slide

  62. class OrderTest < Minitest::Test
    def setup
    @order = Order.new
    end
    def around(&block)
    Order.stub :can_be_processed_today?, true, &block
    end
    def test_initiated_at
    assert_equal Date.today, @order.initiated_at.to_date
    end
    def test_add_product
    product = Product.new
    @order << product
    assert_equal [product], @order.products
    assert_equal 1, @order.size
    assert [email protected]?
    end

    View Slide

  63. def test_add_product
    product = Product.new
    @order << product
    assert_equal [product], @order.products
    assert_equal 1, @order.size
    assert [email protected]?
    end
    def test_valid
    @order.user = User.new
    @order << Product.new
    assert @order.valid?
    end
    def test_valid_when_no_user
    @order << Product.new
    assert [email protected]?
    end

    View Slide

  64. def test_valid_when_no_user
    @order << Product.new
    assert [email protected]?
    end
    def test_valid_when_no_products
    @order.user = User.new
    assert [email protected]?
    end
    def test_cancel
    @order.cancel
    assert_equal :canceled, @order.state
    end
    end

    View Slide

  65. class OrderTest < Minitest::Test
    def setup
    @order = Order.new
    end
    def around(&block)
    Order.stub :can_be_processed_today?, true, &block
    end
    def test_initiated_at
    assert_equal Date.today, @order.initiated_at.to_date
    end
    def test_add_product
    product = Product.new
    @order << product
    assert_equal [product], @order.products
    assert_equal 1, @order.size
    assert [email protected]?
    end
    def test_valid
    @order.user = User.new
    @order << Product.new
    assert @order.valid?
    end
    def test_valid_when_no_user
    @order << Product.new
    assert [email protected]?
    end
    def test_valid_when_no_products
    @order.user = User.new
    assert [email protected]?
    end
    def test_cancel
    @order.cancel
    assert_equal :canceled, @order.state
    end
    end

    View Slide

  66. View Slide

  67. Какво трябва да
    тестваме?

    View Slide

  68. Query method
    car.top_speed
    user.name
    array.empty?

    View Slide

  69. Command method
    car.drive_to(supermarket)
    user.destroy!
    array << “1”

    View Slide

  70. Object
    Incomming Outgoing
    Send to self

    View Slide

  71. Incomming Send to self Outgoing
    object.method()
    def method
    private_method()
    end
    def other_method(object)
    object.method()
    end

    View Slide

  72. Query Command
    ▶︎ Incomming
    Check result
    directly
    Check side effects
    via public api
    Send to self
    (private)

    No tests

    No tests
    ◀ Outgoing
    ✕ 

    No tests
    Check with expect to
    send (mock)

    View Slide

  73. ▶︎ Incomming query
    class ArrayTest < Minitest::Test
    def test_empty
    array = [1]
    assert !array.empty?
    end

    end

    View Slide

  74. ▶︎ Incomming command
    class ArrayTest < Minitest::Test
    def test_empty
    array = [1]
    assert !array.empty?
    end
    end

    View Slide

  75. ◀ Outgoing command
    class MyObjectTest < Minitest::Test
    def test_my_method
    array = Minitest::Mock.new
    array.expect :clear, :returned
    custom = MyObject.new
    custom.clear array
    array.verify
    end
    end

    View Slide

  76. RSpec

    View Slide

  77. Dave Astels
    David Chelimsky

    release as 0.0.10 - July 2, 2008

    View Slide

  78. class ArrayTest < Minitest::Test
    def setup
    @array = []
    end
    def test_empty
    assert @array.empty?
    end
    def test_push
    @array << 1
    assert_equal [1], @array
    end
    end

    View Slide

  79. describe Array do
    before do
    @array = []
    end
    it "can tell when it is empty" do
    expect(@array).to be_empty
    end
    it "can filled with items" do
    @array << 1
    expect(@array).to eq [1]
    end
    end

    View Slide

  80. describe Array do
    before do
    @array = []
    end
    it "can tell when it is empty" do
    expect(@array).to be_empty
    end
    it "can filled with items" do
    @array << 1
    expect(@array).to eq [1]
    end
    end
    Example
    Group

    View Slide

  81. describe Array do
    before do
    @array = []
    end
    it "can tell when it is empty" do
    expect(@array).to be_empty
    end
    it "can filled with items" do
    @array << 1
    expect(@array).to eq [1]
    end
    end

    View Slide

  82. describe Array do
    before do
    @array = []
    end
    it "can tell when it is empty" do
    expect(@array).to be_empty
    end
    it "can filled with items" do
    @array << 1
    expect(@array).to eq [1]
    end
    end
    Setup

    View Slide

  83. describe Array do
    before do
    @array = []
    end
    it "can tell when it is empty" do
    expect(@array).to be_empty
    end
    it "can filled with items" do
    @array << 1
    expect(@array).to eq [1]
    end
    end

    View Slide

  84. describe Array do
    before do
    @array = []
    end
    it "can tell when it is empty" do
    expect(@array).to be_empty
    end
    it "can filled with items" do
    @array << 1
    expect(@array).to eq [1]
    end
    end
    Example

    View Slide

  85. describe Array do
    before do
    @array = []
    end
    it "can tell when it is empty" do
    expect(@array).to be_empty
    end
    it "can filled with items" do
    @array << 1
    expect(@array).to eq [1]
    end
    end

    View Slide

  86. describe Array do
    before do
    @array = []
    end
    it "can tell when it is empty" do
    expect(@array).to be_empty
    end
    it "can filled with items" do
    @array << 1
    expect(@array).to eq [1]
    end
    end
    Expectation

    View Slide

  87. describe Array do
    before do
    @array = []
    end
    it "can tell when it is empty" do
    expect(@array).to be_empty
    end
    it "can filled with items" do
    @array << 1
    expect(@array).to eq [1]
    end
    end

    View Slide

  88. describe Array do
    let(:array) { [] }
    it "can tell when it is empty" do
    expect(array).to be_empty
    end
    it "can filled with items" do
    array << 1
    expect(array).to eq [1]
    end
    end

    View Slide

  89. describe Array do
    let(:array) { [] }
    it "can tell when it is empty" do
    expect(array).to be_empty
    end
    it "can filled with items" do
    array << 1
    expect(array).to eq [1]
    end
    end

    View Slide

  90. describe Array do
    describe "#empty?" do
    it "returns true when empty" do
    expect(array).to be_empty
    end
    it "returns false when not empty" do
    array = [1]
    expect(array).to be_empty
    end
    end
    end

    View Slide

  91. View Slide

  92. Mocks

    View Slide

  93. View Slide

  94. Fake
    Used as a simpler implementation, e.g. using an in-memory
    database in the tests instead of doing real database access.

    View Slide

  95. Dummy
    Used when a parameter is needed for the tested method but
    without actually needing to use the parameter.

    View Slide

  96. Stub
    Test stubs are objects with pre-programmed behaviour.

    View Slide

  97. Spy
    Records arguments, return value, the value of this and
    exception thrown (if any) for all its calls.

    View Slide

  98. Mock
    Mocks are fake methods (like spies) with pre-programmed
    behaviour (like stubs) as well as pre-programmed
    expectations.
    A mock will fail your test if it is not used as expected.

    View Slide

  99. Test double
    Stub
    Spy
    Mock
    Fake
    Dummy

    View Slide

  100. describe PostsController do
    stub_rendering
    stub_current_user
    let(:post) { double 'Post' }
    describe "GET 'index'" do
    it "responds with posts of a given category" do
    allow(Post).to receive(:for_category).with(‘1’).and_return [post]
    get :index, category_id: '1'
    expect(controller).to respond_with [post]
    end
    end
    describe "POST 'create'" do
    it "creates new post" do
    allow(Post).to receive(:create).and_return post
    post :create, post: {title: 'test'}
    expect(Post).to have_received(:create).with(user: current_user, title: 'tes
    end
    end
    end

    View Slide

  101. Test Driven
    Development

    View Slide

  102. Automated
    Testing
    Test Driven
    Development

    View Slide

  103. Test Driven Development

    View Slide

  104. Test Driven Development
    1 Добавя се тест
    ... за несъществуващ код

    View Slide

  105. Test Driven Development
    1 Добавя се тест
    ... за несъществуващ код
    2 Пише се код
    ... колкото само тестът да мине

    View Slide

  106. Test Driven Development
    1 Добавя се тест
    ... за несъществуващ код
    3 Правят се подобрения
    ... подобрява се качеството на кода
    2 Пише се код
    ... колкото само тестът да мине

    View Slide

  107. Test Driven Development
    1 Добавя се тест
    ... за несъществуващ код
    3 Правят се подобрения
    ... подобрява се качеството на кода
    2 Пише се код
    ... колкото само тестът да мине

    View Slide

  108. describe Calculation do
    it "can sum numbers" do
    calculation = Calculation.new('1 + 1')
    expect(calculation.value).to eq 2
    end
    end

    View Slide

  109. uninitialized constant Calculation (NameError)

    View Slide

  110. class Calculation
    def initialize(expression)
    end
    def value
    end
    end

    View Slide

  111. View Slide

  112. class Calculation
    def initialize(expression)
    @expression = expression
    end
    def value
    a, b = @expression.split '+'
    a.to_i + b.to_i
    end
    end

    View Slide

  113. View Slide

  114. describe Calculation do
    # . . .
    it "can subtract numbers" do
    calculation = Calculation.new('2 - 1')
    expect(calculation.value).to eq 1
    end
    end

    View Slide

  115. View Slide

  116. class Calculation
    def initialize(expression)
    @expression = expression
    end
    def value
    a, operation, b = @expression.split ' '
    if operation == '+'
    a.to_i + b.to_i
    elsif operation == '-'
    a.to_i - b.to_i
    end
    end
    end

    View Slide

  117. View Slide

  118. describe Calculation do
    # . . .
    it "can multiply numbers" do
    calculation = Calculation.new('3 * 2')
    expect(calculation.value).to eq 6
    end
    end

    View Slide

  119. View Slide

  120. class Calculation
    def initialize(expression)
    @expression = expression
    end
    def value
    a, operation, b = @expression.split ' '
    if operation == '+'
    a.to_i + b.to_i
    elsif operation == '-'
    a.to_i - b.to_i
    elsif operation == '*'
    a.to_i * b.to_i
    end
    end
    end

    View Slide

  121. View Slide

  122. describe Calculation do
    # . . .
    it "raises an error on unknown operation" do
    calculation = Calculation.new('3 ~ 2')
    expect { calculation.value }.to raise_error
    end
    end

    View Slide

  123. class Calculation
    def initialize(expression)
    @expression = expression
    end
    def value
    a, operation, b = @expression.split ' '
    if operation == '+'
    a.to_i + b.to_i
    elsif operation == '-'
    a.to_i - b.to_i
    elsif operation == '*'
    a.to_i * b.to_i
    else
    raise "Can't parse expression - #{@expression}"
    end
    end
    end

    View Slide

  124. class Calculation
    def initialize(expression)
    @expression = expression
    end
    OPERATIONS = {
    '+' => ->(a, b) { a + b },
    '-' => ->(a, b) { a - b },
    '*' => ->(a, b) { a * b }
    }
    def value
    a, operand, b = @expression.split ' '
    operation = OPERATIONS[operand]
    raise "Can't parse expression - #{@expression}" unless operation
    operation.call a.to_i, b.to_i
    end
    end

    View Slide

  125. describe Calculation do
    # . . .
    it "can divide numbers" do
    calculation = Calculation.new('6 / 3')
    expect(calculation.value).to eq 2
    end
    end

    View Slide

  126. View Slide

  127. class Calculation
    def initialize(expression)
    @expression = expression
    end
    OPERATIONS = {
    '+' => ->(a, b) { a + b },
    '-' => ->(a, b) { a - b },
    '*' => ->(a, b) { a * b },
    '/' => ->(a, b) { a / b }
    }
    def value
    a, operand, b = @expression.split ' '
    operation = OPERATIONS[operand]
    raise "Can't parse expression - #{@expression}" unless operation
    operation.call a.to_f, b.to_f
    end
    end

    View Slide

  128. View Slide

  129. class Calculation
    OPERATIONS = {
    '+' => ->(a, b) { a + b },
    '-' => ->(a, b) { a - b },
    '*' => ->(a, b) { a * b },
    '/' => ->(a, b) { a / b }
    }
    attr_reader :expression
    def initialize(expression)
    @expression = expression
    end
    def value
    @value ||= calculate_value
    end
    def to_s
    “#{expression} = #{value}"
    end
    private
    def calculate_value
    left, operand, right = expression.split ' '
    operation = OPERATIONS[operand]
    raise "Can't parse expression - #{@expression}" unless operation
    operation.call left.to_f, right.to_f
    end
    end

    View Slide

  130. Fragile Tests

    View Slide

  131. Bad fixture
    (fragile test)

    View Slide

  132. class OrderTest < Minitest::Test
    def test_expired
    order = Order.new(
    user: User.new,
    product: [Product.new]
    town: 'Sofia'
    initiated_at: 5.days.ago
    )
    assert order.expired?
    end
    end

    View Slide

  133. class OrderTest < Minitest::Test
    def test_expired
    order = create_order initiated_at: 5.days.ago
    assert order.expired?
    end
    end

    View Slide

  134. Behaviour changes
    (fragile test)

    View Slide

  135. class OrderTest < Minitest::Test
    def setup
    @order = Order.new
    end
    def test_valid
    @order.user = User.new
    @order << Product.new
    assert @order.valid?
    end
    def test_valid_when_no_user
    @order << Product.new
    assert [email protected]?
    end
    def test_valid_when_no_products
    @order.user = User.new
    assert [email protected]?
    end
    end

    View Slide

  136. class OrderTest < Minitest::Test
    def setup
    @order = Order.new(
    user: User.new,
    products: [Product.new]
    )
    end
    def test_valid
    assert @order.valid?
    end
    def test_valid_when_no_user
    @order.user = nil
    assert [email protected]?
    end
    def test_valid_when_no_products
    @order.products = []
    assert [email protected]?
    end
    end

    View Slide

  137. Indirection
    (fragile test)

    View Slide

  138. describe "Task" do
    describe ".complete_all" do
    it "completes a task by text mask" do
    task = create_task text: 'Create slides'
    other = create_task text: 'Create examples'
    Task.complete_all query: 'slides'
    expect(Task.ongoing).to eq [task]
    end
    end
    end

    View Slide

  139. describe "Task" do
    describe ".complete_all" do
    it "completes tasks who match query" do
    task = create_task text: 'Create slides'
    Task.complete_all query: 'slides'
    expect { task.reload }.to change { task.completed? }.to true
    end
    it "doesn't complete tasks who don't match query" do
    task = create_task text: 'Create examples'
    Task.complete_all query: 'slides'
    expect { task.reload }.not_to change { task.completed? }
    end
    end
    end

    View Slide

  140. describe "Task" do
    # . . .
    describe ".ongoing" do
    it "selects ongoing tasks" do
    ongoing = create_task state: :ongoing
    completed = create_task state: :completed
    expect(Task.ongoing).to eq ongoing
    end
    end
    end

    View Slide

  141. Coupling
    (fragile test)

    View Slide

  142. describe Wallet do
    describe "#buy" do
    amount = true
    gateway = double 'Gateway'
    processor = double 'Processor'
    allow(gateway).to receive(:active?).and_return true
    allow(gateway).to receive(:processor).and_return processor
    allow(processor).to receive(:enough_money?).and_return true
    allow(processor).to receive(:process)
    Wallet.new(gateway).buy amount: 5
    expect(processor).to have_received(:process).with(5)
    end
    end

    View Slide

  143. class Wallet
    def initialize(gateway)
    @gateway = gateway
    end
    def buy(amount: amount)
    return false if @gateway.active?
    processor = @gateway.processor
    return false if processor.enough_money?(amount)
    processor.process amount
    end
    end

    View Slide

  144. describe Wallet do
    describe "#buy" do
    amount = true
    processor = double 'Processor'
    allow(gateway).to receive(:process)
    Wallet.new(gateway).buy amount: 5
    expect(processor).to have_received(:process).with(5)
    end
    end

    View Slide

  145. class Wallet
    def initialize(gateway)
    @gateway = gateway
    end
    def buy(amount: amount)
    @gateway.process amount
    end
    end

    View Slide

  146. Tools

    View Slide

  147. • Rspec
    • FactoryGirl
    • Timecop
    • DatabaseCleaner
    • Capybara

    View Slide

  148. Книги

    View Slide

  149. View Slide

  150. View Slide

  151. Обобщение

    View Slide

  152. • За какво са ни нужни тестове?
    • Какво е автоматичен тест?
    • Какви видове тестове има?
    • Какво е добър тест?
    • Как да пишем добри тестове?
    • Какво трябва да тестваме?
    • Какво са “Mocks”?
    • Какво е “RSpec”?
    • Какво е “Test Driven Development”?
    • Какво е “Fragile tests”?

    View Slide

  153. https://speakerdeck.com/rstankov/testing-ruby

    View Slide

  154. https://github.com/rstankov/talks-code

    View Slide

  155. View Slide

  156. View Slide

  157. @rstankov
    Благодаря за вниманието :)

    View Slide

  158. Въпроси?

    View Slide