Testing Your Code

Robert Mosolgo

The Goal

🚦

📈

📖

 

🐛🔨

💰

Anatomy of a Unit Test

  • Fixture
  • Test Case 
  • Assertion 

 

  • (Test Suite)

Anatomy of a Unit Test

# in addition.rb
class Addition 
  def initialize(inputs: )
    @inputs = inputs 
  end

  def value 
    sum = 0
    @inputs.each { |input| sum += input }
    sum 
  end 
end
# in addition_test.rb
class AdditionTest < Minitest::Test

  def setup 
    @numbers = [1, 2, 3]
  end

  def test_result_equals_the_sum 
    addition = Addition.new(input: @numbers)
    value = addition.value 

    assert(value == 6)
  end
end

Test case

Fixtures

Assertion

MMaaS 

Double Duty

Mirror, Mirror

Single Responsibility?

class BadExpression

  def initialize(operation_name, inputs)
    @operation_name = operation_name
    @inputs = inputs
  end

  def value
    method_name = "calculate_"+ @operation_name
    send(method_name)
  end

  private

  def calculate_largest_even
    @inputs
      .select {|i| i % 2 == 0}
      .max
  end

  def calculate_double
    calculate_addition * 2
  end

  def calculate_addition
    @inputs.reduce(&:+)
  end
end
class BadExpressionTest < Minitest::Test
  def setup
    @inputs = [2,3,4]
  end

  def test_it_does_addition
    # ...
  end

  def test_it_does_multiplication
    # ...
  end

  def test_it_does_double
    # ...
  end

  def test_it_does_largest_even
    # ...
  end
end

Interface vs Implementation

# Return the largest multiple of 2 
# from the inputs
class LargestEven
  EVEN_NUMBERS = [
    2,  4,  6,  8, 10,
   12, 14, 16, 18, 20,
  ]

  def initialize(inputs: )
    @inputs = inputs
  end

  def value
    evens = @inputs & EVEN_NUMBERS 
    evens.max
  end

  private_constant :EVEN_NUMBERS
end


class LargestEvenTest < Minitest::Test
  def setup
    @numbers = [2, 3, 4, 5]
  end

  def test_result_equals_the_sum
    addition = LargestEven.new(
      inputs: @numbers
    )

    value = addition.value

    assert_equal(4, value)
  end
end
# Return the largest multiple of 2 
# from the inputs
class LargestEven
  def initialize(inputs: )
    @inputs = inputs
  end

  def value
    evens = @inputs.select { |i| i.even? }
    evens.max
  end
end


class LargestEvenTest < Minitest::Test
  def setup
    @numbers = [2, 3, 4, 5]
    @large_numbers = [
      100, 505, 600, 437
    ]
  end

  def test_result_equals_the_sum
    addition = LargestEven.new(
      inputs: @numbers
    )
    value = addition.value
    assert_equal(5, value)
  end

  def test_result_may_be_very_large
    addition = LargestEven.new(
        inputs: @large_numbers
    )
    value = addition.value
    assert_equal(600, value)   
  end
end

Tests are Docs

👶💭  🔜  

 

    💁  🔜  

👴❓

 

 👬👫

Easy Tests 🍻 Good Code

Short, fast, readable tests 😊

Small units of work 🔨

Insulated behaviors 🔐

Comprehensive tests 🚦

Living documentation 📖

Confident changes 📈

Further Reading 

Kent Beck, "RIP, Test-Driven Development"

 

Test Framework of Your Choice

 

Testing Your Code

By Robert Mosolgo

Testing Your Code

Test your code with more code! 🎉 (Full Stack North County, October 8, 2015)

  • 1,286