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

Testing 201

Testing 201

Joseph Mastey

April 15, 2019
Tweet

More Decks by Joseph Mastey

Other Decks in Technology

Transcript

  1. TESTING 201
    OR: GREAT EXPECTATIONS
    JOE MASTEY, APRIL 2019

    View full-size slide

  2. EVERYONE IS
    DOING THEIR BEST

    View full-size slide

  3. HEURISTICS,
    NOT RULES

    View full-size slide

  4. THIS STUFF SHOULDN’T
    SLOW YOU DOWN

    View full-size slide

  5. PROLOGUE: THE TROUBLE
    WITH A SIMPLE TEST

    View full-size slide

  6. class CreateShipmentHistoryReport
    attr_accessor :user, :shipments
    def initialize(user)
    @user = user
    @shipments = user.shipments
    end
    def process
    shipments.sort_by(&:created_at)
    data = shipments.map do |shipment|
    shipment.to_json.values +
    [ shipment.user.name,
    shipment.product.name ]
    end
    file = CSV.open(tmp_filename) do |csv|
    csv << shipments.first.to_json.keys + ['user', 'product']
    data.map do |row|
    csv << row
    end
    end
    persist_csv_to_s3(file)
    notify_user(user.email)
    file
    end
    end

    View full-size slide

  7. class CreateShipmentHistoryReport
    attr_accessor :user, :shipments
    def initialize(user)
    @user = user
    @shipments = user.shipments
    end

    View full-size slide

  8. def process
    shipments.sort_by(&:created_at)
    data = shipments.map do |shipment|
    shipment.to_json.values +
    [ shipment.user.name,
    shipment.product.name ]
    end
    # continued below...

    View full-size slide

  9. # ... process continued
    file = CSV.open(tmp_filename) do |csv|
    csv << shipments.first.to_json.keys + ['user', 'product']
    data.map do |row|
    csv << row
    end
    end
    persist_csv_to_s3(file)
    notify_user(user.email)
    file
    end

    View full-size slide

  10. class CreateShipmentHistoryReport
    attr_accessor :user, :shipments
    def initialize(user)
    @user = user
    @shipments = user.shipments
    end
    def process
    shipments.order('created_at desc')
    data = shipments.map do |shipment|
    shipment.to_json.values +
    [ shipment.user.name,
    shipment.product.name ]
    end
    file = CSV.open(tmp_filename) do |csv|
    csv << shipments.first.to_json.keys + ['user', 'product']
    data.map do |row|
    csv << row
    end
    end
    persist_csv_to_s3(file)
    notify_user(user.email)
    file
    end
    end

    View full-size slide

  11. describe CreateShipmentHistoryReport do
    describe "#process" do
    it "generates an array of strings" do
    expect(csv_data.length).to eq(shipments.length)
    expect(csv_data).to all(be_an(Array))
    end
    it "generates an output record for each input, sorted by date" do
    expect(csv_data).to eq(output_records)
    end
    end
    end

    View full-size slide

  12. let(:subject) { described_class.new(user).process }
    let(:csv_data) { CSV.parse(subject) }

    View full-size slide

  13. let(:user) { create(:user, :signup_complete) }
    let(:subscription) { create(:subscription) }
    before do
    user.subscription << subscription
    end

    View full-size slide

  14. let(:product) { create(:product) }
    let!(:shipment) { create(:shipment,
    product: product,
    created_at: 2.days.ago) }
    let(:output_records) do
    [
    [shipment.created_on, shipment.completed_on,
    user.name, product.name]
    ]
    end
    before do
    expect(user)
    .to receive(:shipments)
    .and_return([shipment])
    end
    end

    View full-size slide

  15. before do
    expect_any_instance_of(described_class)
    .to receive(:save_to_s3)
    .and_return(true)
    expect_any_instance_of(described_class)
    .to receive(:email_user)
    .and_return(true)
    end

    View full-size slide

  16. let(:product_1) { create(:product) }
    let(:product_2) { create(:product) }
    let(:product_3) { create(:product) }
    let(:product_4) { create(:product) }

    View full-size slide

  17. let!(:shipment_1) { create(:shipment, product: product_1,
    created_at: 2.days.ago) }
    let!(:shipment_2) { create(:shipment, product: product_2,
    created_at: 3.days.ago) }
    let!(:shipment_3) { create(:shipment, product: product_3,
    created_at: 4.days.ago) }
    let!(:shipment_4) { create(:shipment, product: product_4,
    created_at: 5.days.ago) }

    View full-size slide

  18. let(:shipments) do
    [
    shipment_1,
    shipment_2,
    shipment_3,
    shipment_4
    ]
    end

    View full-size slide

  19. let(:output_records) do
    [
    [shipment_4.created_on, shipment_4.completed_on,
    user.name, product_4.name],
    [shipment_3.created_on, shipment_3.completed_on,
    user.name, product_3.name],
    [shipment_2.created_on, shipment_2.completed_on,
    user.name, product_2.name],
    [shipment_1.created_on, shipment_1.completed_on,
    user.name, product_1.name],
    ]
    end

    View full-size slide

  20. it "generates an output record for each input, sorted by date" do
    expect(csv_data).to eq(output_records)
    end

    View full-size slide

  21. describe CreateShipmentHistoryReport do
    describe "#process" do
    let(:user) { create(:user, :signup_complete) }
    let(:subscription) { create(:subscription) }
    let(:product_1) { create(:product) }
    let(:product_2) { create(:product) }
    let(:product_3) { create(:product) }
    let(:product_4) { create(:product) }
    let!(:shipment_1) { create(:shipment, product: product_1,
    created_at: 2.days.ago) }
    let!(:shipment_2) { create(:shipment, product: product_2,
    created_at: 3.days.ago) }
    let!(:shipment_3) { create(:shipment, product: product_3,
    created_at: 4.days.ago) }
    let!(:shipment_4) { create(:shipment, product: product_4,
    created_at: 5.days.ago) }
    let(:subject) { described_class.new(user).process }
    let(:csv_data) { CSV.parse(subject) }
    let(:shipments) do
    [
    shipment_1,
    shipment_2,
    shipment_3,
    shipment_4
    ]
    end
    let(:output_records) do
    [
    [shipment_4.created_on, shipment_4.completed_on,
    user.name, product_4.name],
    [shipment_3.created_on, shipment_3.completed_on,
    user.name, product_3.name],
    [shipment_2.created_on, shipment_2.completed_on,
    user.name, product_2.name],
    [shipment_1.created_on, shipment_1.completed_on,
    user.name, product_1.name],
    ]
    end
    before do
    user.subscription << subscription
    expect(user)
    .to receive(:shipments)
    .and_return(shipments)
    expect_any_instance_of(described_class)
    .to receive(:save_to_s3)
    .and_return(true)
    expect_any_instance_of(described_class)
    .to receive(:email_user)
    .and_return(true)
    end
    it "generates an array of strings" do
    expect(csv_data.length).to eq(shipments.length)
    expect(csv_data).to all(be_an(Array))
    end
    it "generates an output record for each input, sorted by
    date" do
    expect(csv_data).to eq(output_records)
    end
    end
    end

    View full-size slide

  22. FOCUSING ON THE
    WRONG THINGS

    View full-size slide

  23. THE 3 ROLES
    OF TESTING (IN ORDER)

    View full-size slide

  24. 1. DESIGN FEEDBACK
    2. DOCUMENTATION
    3. VERIFICATION

    View full-size slide

  25. TESTS AS
    DESIGN FEEDBACK

    View full-size slide

  26. A NOTE ON TDD

    View full-size slide

  27. CODE THAT’S DIFFICULT TO
    TEST IS TRYING TO TELL YOU
    SOMETHING IMPORTANT

    View full-size slide

  28. IT’S EASIER TO WORK WITH
    OBJECTS IN SMALL CHUNKS.

    View full-size slide

  29. let(:csv_data) { CSV.parse(subject) }
    before do
    expect_any_instance_of(described_class)
    .to receive(:save_to_s3)
    .and_return(true)
    expect_any_instance_of(described_class)
    .to receive(:email_user)
    .and_return(true)
    end

    View full-size slide

  30. before do
    expect_any_instance_of(described_class)
    .to receive(:save_to_s3)
    .and_return(true)
    expect_any_instance_of(described_class)
    .to receive(:email_user)
    .and_return(true)
    end

    View full-size slide

  31. def data(shipments)
    shipments.sort_by!(&:created_at)
    shipments.map do |shipment|
    shipment.to_json.values +
    [ shipment.user.name,
    shipment.product.name ]
    end
    end
    def headers
    shipments.first.to_json.keys + ['user', 'product']
    end

    View full-size slide

  32. file = CSV.open(tmp_filename) do |csv|
    csv << headers
    data(shipments).map { |row| csv << row }
    end

    View full-size slide

  33. it "generates an output record for each input, sorted by date" do
    subject = described_class.new(user)
    expect(subject.data).to eq(output_records)
    end

    View full-size slide

  34. before do
    # expect_any_instance_of(described_class)
    # .to receive(:save_to_s3)
    # .and_return(true)
    # expect_any_instance_of(described_class)
    # .to receive(:email_user)
    # .and_return(true)
    end

    View full-size slide

  35. before do
    expect(user)
    .to receive(:shipments)
    .and_return(shipments)
    end

    View full-size slide

  36. class CreateShipmentHistoryReport
    def initialize(user, shipments: user.shipments)
    @user = user
    @shipments = shipments
    end
    end

    View full-size slide

  37. before do
    # expect(user)
    # .to receive(:shipments)
    # .and_return(shipments)
    end
    described_class.new(user, shipments)

    View full-size slide

  38. def data(shipments)
    shipments.sort_by!(&:created_at)
    shipments.map do |shipment|
    shipment.to_json.values +
    [ shipment.user.name,
    shipment.product.name ]
    end
    end
    def headers
    shipments.first.to_json.keys + ['user', 'product']
    end

    View full-size slide

  39. def serialize(shipment)
    {
    created_on: shipment.created_on,
    completed_on: shipment.completed_on,
    user: shipment.user.name,
    product: shipment.product.name,
    }
    end

    View full-size slide

  40. def serialize(shipment)
    {
    created_on: shipment.created_on,
    completed_on: shipment.completed_on,
    user: shipment.user.name,
    product: shipment.product.name,
    }
    end
    def data(shipments)
    shipments.sort_by(&:created_at).map do |shipment|
    serialize(shipment)
    end
    end
    def headers
    serialize(shipments.first).keys
    end

    View full-size slide

  41. describe "#serialize" do
    it "serializes some shipment and user data" do
    shipment = create_shipment(date: 2.days.ago)
    report = described_class.new(user, [])
    result = report.serialize(shipment)
    expect(result).to eq({
    created_on: shipment.created_on,
    completed_on: shipment.completed_on,
    user: shipment.user.name,
    product: shipment.product.name,
    })
    end
    end

    View full-size slide

  42. # let(:output_records) do
    # [
    # [shipment_4.created_on, shipment_4.completed_on,
    # user.name, product_4.name],
    # [shipment_3.created_on, shipment_3.completed_on,
    # user.name, product_3.name],
    # [shipment_2.created_on, shipment_2.completed_on,
    # user.name, product_2.name],
    # [shipment_1.created_on, shipment_1.completed_on,
    # user.name, product_1.name],
    # ]
    # end

    View full-size slide

  43. describe "ordering" do
    it "reorders shipments by their creation date" do
    shipment_1, shipment_2
    report = described_class.new(user, [shipment_1, shipment_2])
    result = report.data
    expect(result.map(&:first)).to eq(
    [shipment_2.created_on, shipment_1.created_on]
    )
    end
    end

    View full-size slide

  44. CREATE AS FEW RECORDS
    AS YOU CAN (AND ONLY
    RELATED ONES)

    View full-size slide

  45. let(:user) { create(:user, :signup_complete) }
    let(:subscription) { create(:subscription) }
    before do
    user.subscription << subscription
    end

    View full-size slide

  46. Factory AR records AR queries
    create(:user) 1 10
    create(:meal) 5 41
    create(:full_menu) 94 584
    create(:weekly_basket) 104 644
    create(:user, :with_order_history) 379 2336

    View full-size slide

  47. let(:user) do
    instance_double(User, email: "[email protected]", shipments: shipments)
    end

    View full-size slide

  48. let(:user) { User.new(email: "[email protected]", name: "Joe") }

    View full-size slide

  49. describe "ordering" do
    it "reorders shipments by their creation date" do
    shipment_1, shipment_2
    report = described_class.new(user, [shipment_1, shipment_2])
    result = report.data
    expect(result.map(&:first)).to eq(
    [shipment_2.created_on, shipment_1.created_on]
    )
    end
    end

    View full-size slide

  50. PAY ATTENTION TO
    SIMILARITY AND
    DIFFERENCE

    View full-size slide

  51. let(:product_1) { create(:product) }
    let(:product_2) { create(:product) }
    let(:product_3) { create(:product) }
    let(:product_4) { create(:product) }

    View full-size slide

  52. let!(:shipment_1) { create(:shipment, product: product_1,
    created_at: 2.days.ago) }
    let!(:shipment_2) { create(:shipment, product: product_2,
    created_at: 3.days.ago) }
    let!(:shipment_3) { create(:shipment, product: product_3,
    created_at: 4.days.ago) }
    let!(:shipment_4) { create(:shipment, product: product_4,
    created_at: 5.days.ago) }

    View full-size slide

  53. let(:shipments) do
    [
    shipment_1,
    shipment_2,
    shipment_3,
    shipment_4
    ]
    end

    View full-size slide

  54. let(:output_records) do
    [
    [shipment_4.created_on, shipment_4.completed_on,
    user.name, product_4.name],
    [shipment_3.created_on, shipment_3.completed_on,
    user.name, product_3.name],
    [shipment_2.created_on, shipment_2.completed_on,
    user.name, product_2.name],
    [shipment_1.created_on, shipment_1.completed_on,
    user.name, product_1.name],
    ]
    end

    View full-size slide

  55. def create_shipment(date:)
    product = create(:product)
    create(:shipment, product: product, created_at: date)
    end

    View full-size slide

  56. describe "ordering" do
    it "reorders shipments by their creation date" do
    old = create_shipment(date: 9.days.ago)
    new = create_shipment(date: 2.days.ago)
    report = described_class.new(user, [new, old])
    result = report.data
    expect(result.map(&:first)).to eq(
    [old.created_on, new.created_on]
    )
    end
    end

    View full-size slide

  57. # let(:product_1) { create(:product) }
    # let(:product_2) { create(:product) }
    # let(:product_3) { create(:product) }
    # let(:product_4) { create(:product) }
    # let!(:shipment_1) { create(:shipment,
    product: product_1,
    # created_at:
    2.days.ago) }
    # let!(:shipment_2) { create(:shipment,
    product: product_2,
    # created_at:
    3.days.ago) }
    # let!(:shipment_3) { create(:shipment,
    product: product_3,
    # created_at:
    4.days.ago) }
    # let!(:shipment_4) { create(:shipment,
    product: product_4,
    # created_at:
    5.days.ago) }
    #
    # let(:shipments) do
    # [
    # shipment_1,
    # shipment_2,
    # shipment_3,
    # shipment_4
    # ]
    # end
    # let(:output_records) do
    # [
    # [shipment_4.created_on,
    shipment_4.completed_on,
    # user.name, product_4.name],
    # [shipment_3.created_on,
    shipment_3.completed_on,
    # user.name, product_3.name],
    # [shipment_2.created_on,
    shipment_2.completed_on,
    # user.name, product_2.name],
    # [shipment_1.created_on,
    shipment_1.completed_on,
    # user.name, product_1.name],
    # ]
    # end

    View full-size slide

  58. TRY TO REDUCE
    COMPLEXITY, NOT HIDE IT

    View full-size slide

  59. def create_shipment(date:)
    product = create(:product)
    create(:shipment, product: product, created_at: date)
    end

    View full-size slide

  60. it "generates an output record for each input, sorted by date" do
    expect(csv_data).to eq(output_records)
    end

    View full-size slide

  61. DON’T DO TOO MUCH
    WORK

    View full-size slide

  62. def process
    def serialize(shipment)
    def data(shipments)
    def headers
    def save_to_s3(file)
    def notify_user(email)

    View full-size slide

  63. TESTS AS
    DOCUMENTATION

    View full-size slide

  64. WRITE TESTS FOR HUMANS,
    NOT FOR THE COMPILER

    View full-size slide

  65. it "generates an output record for each input, sorted by date" do
    expect(csv_data).to eq(output_records)
    end

    View full-size slide

  66. describe "ordering" do
    it "reorders shipments by their creation date" do
    old = create_shipment(date: 9.days.ago)
    new = create_shipment(date: 2.days.ago)
    report = described_class.new(user, [new, old])
    result = report.data
    expect(result.map(&:first)).to eq(
    [old.created_on, new.created_on]
    )
    end
    end

    View full-size slide

  67. it "serializes data as an array" do
    users = create_list(:user, 3)
    response = subject.create(User.all).process
    response = response[0][4]
    expect(response).to eq(users.first.name)
    end

    View full-size slide

  68. it "serializes data as an array" do
    users = create_list(:user, 3)
    serialized_users = subject.create(User.all).to_hashes
    response = serialized_users.first.full_name
    expect(response).to eq(users.first.name)
    end

    View full-size slide

  69. TRY TO STICK TO A GIVEN/
    WHEN/THEN STRUCTURE

    View full-size slide

  70. describe "ordering" do
    it "reorders shipments by their creation date" do
    old = create_shipment(date: 9.days.ago)
    new = create_shipment(date: 2.days.ago)
    report = described_class.new(user, [new, old])
    result = report.data
    expect(result.map(&:first)).to eq(
    [old.created_on, new.created_on]
    )
    end
    end

    View full-size slide

  71. describe "ordering" do
    it "reorders shipments by their creation date" do
    # Given
    old = create_shipment(date: 9.days.ago)
    new = create_shipment(date: 2.days.ago)
    # When
    report = described_class.new(user, [new, old])
    result = report.data
    # Then
    expect(result.map(&:first)).to eq(
    [old.created_on, new.created_on]
    )
    end
    end

    View full-size slide

  72. it "reorders shipments by their creation date" do
    old = create_shipment(date: 9.days.ago)
    new = create_shipment(date: 2.days.ago)
    report = described_class.new(user, [new, old])
    expect(report.data.map(&:first)).to eq([old, new])
    end

    View full-size slide

  73. before do
    expect_any_instance_of(described_class)
    .to receive(:save_to_s3)
    .and_return(true)
    expect_any_instance_of(described_class)
    .to receive(:email_user)
    .and_return(true)
    end

    View full-size slide

  74. USING G/W/T EMPHASIZES
    SAMENESS AND
    DIFFERENCE.

    View full-size slide

  75. it "returns order units summed, divided by units per shipper" do
    create_store_order(count_units: 4)
    create_store_order(count_units: 8)
    subject = described_class.new(store_orders: store_orders)
    total_shippers = subject.total_shipper_count
    expect(total_shippers).to eq(3)
    end

    View full-size slide

  76. it "returns a ceiling rounded value" do
    create_store_order(count_units: 7)
    subject = described_class.new(store_orders: store_orders)
    total_shippers = subject.total_shipper_count
    expect(total_shippers).to eq(2)
    end

    View full-size slide

  77. let!(:shipment_1) { create(:shipment, product: product_1,
    created_at: 2.days.ago) }
    let!(:shipment_2) { create(:shipment, product: product_2,
    created_at: 3.days.ago) }
    let!(:shipment_3) { create(:shipment, product: product_3,
    created_at: 4.days.ago) }
    let!(:shipment_4) { create(:shipment, product: product_4,
    created_at: 5.days.ago) }

    View full-size slide

  78. TESTS DON’T NEED TO BE
    (TOO) DRY

    View full-size slide

  79. it "returns order units summed, divided by units per shipper" do
    create_store_order(count_units: 4)
    create_store_order(count_units: 8)
    subject = described_class.new(store_orders: store_orders)
    total_shippers = subject.total_shipper_count
    expect(total_shippers).to eq(3)
    end
    it "returns a ceiling rounded value" do
    create_store_order(count_units: 7)
    subject = described_class.new(store_orders: store_orders)
    total_shippers = subject.total_shipper_count
    expect(total_shippers).to eq(2)
    end

    View full-size slide

  80. describe "#total_shipper_count" do
    subject { described_class.new(store_orders: StoreOrder.all) }
    context "even division" do
    let!(:order1) { create_store_order(count_units: 4) }
    let!(:order2) { create_store_order(count_units: 8) }
    let(:expected_total) { 3 }
    it "returns order units summed, divided by units per shipper" do
    expect(subject.total_shipper_count).to eq(expected_total)
    end
    end
    end

    View full-size slide

  81. context "rounding" do
    let!(:order) { create_store_order(count_units: 7) }
    let(:expected_total) { 2 }
    it "returns order units summed, divided by units per shipper" do
    expect(subject.total_shipper_count).to eq(expected_total)
    end
    end

    View full-size slide

  82. subject(:service) { described_class.new }
    it "sends email" do
    expect(service).to receive(:notify_user)
    end
    after do
    service.process
    end

    View full-size slide

  83. THE FEELING OF NEEDING TO
    DRY YOUR TESTS IS
    ACTUALLY DESIGN FEEDBACK

    View full-size slide

  84. SAY WHAT YOU MEAN,
    LITERALLY

    View full-size slide

  85. def user_summary(user)
    "(#{user.id}) #{user.full_name}, #{user.role}"
    end
    it "returns a user summary for printing" do
    user = User.new(id: 5, full_name: "Dave G", role: :admin)
    expected = "(#{user.id}) #{user.full_name}, #{user.role}"
    summary = user_summary(user)
    expect(summary).to eq(expected)
    end

    View full-size slide

  86. it "returns a user summary for printing" do
    user = User.new(id: 5, full_name: "Dave G", role: :admin)
    summary = user_summary(user)
    expect(summary).to eq("(5) Dave G, admin")
    end

    View full-size slide

  87. LEVERAGE YOUR TOOLS TO
    HELP GUIDE READERS

    View full-size slide

  88. describe "#process" do
    it "return value"
    end
    describe "#process" do
    it "returns the number of records correctly persisted"
    end

    View full-size slide

  89. describe CreateShipmentHistoryReport do
    describe "#sorted_shipments" do
    it "returns an empty array when there are no shipments"
    end
    end
    be rspec ./spec/services/create_shipment_history_report_spec.rb
    —format=‘documentation’
    # CreateShipmentHistoryReport#sorted_shipments returns an empty array
    when there are no shipments

    View full-size slide

  90. it “picks randomly, but correctly” do
    double1 = double
    double2 = double
    result = [double1, double2].sample
    expect(result).to eq(double1)
    end
    # expected: #
    # got: #

    View full-size slide

  91. it “picks randomly, but correctly” do
    double1 = double("first record")
    double2 = double("second record”)
    result = [double1, double2].sample
    expect(result).to eq(double1)
    end
    # expected: #
    # got: #

    View full-size slide

  92. it "checks result size" do
    arr = [1,2,3]
    response = arr.length
    expect(response > 3).to be_true
    end
    # expected true
    # got false

    View full-size slide

  93. it "checks result size" do
    arr = [1,2,3]
    response = arr.length
    expect(response).to be > 3
    end
    # expected: > 3
    # got: 3

    View full-size slide

  94. it "checks validity of a record" do
    thing = double(valid?: true)
    expect(thing.valid?).to be_false
    end
    # expected: false
    # got: true

    View full-size slide

  95. it "checks validity of a record" do
    thing = double("order", valid?: true)
    expect(thing).not_to be_valid
    end
    # expected `#.valid?` to return false, got true

    View full-size slide

  96. TESTS AS
    VERIFICATION

    View full-size slide

  97. TEST WHAT’S IMPORTANT,
    RISKY, OR WHAT’S CHEAP

    View full-size slide

  98. YOU CAN’T HAVE
    100% COVERAGE

    View full-size slide

  99. DON’T TEST WHAT YOU
    DON’T OWN

    View full-size slide

  100. validate :status, presence: true
    it { should validate_presence_of(:status) }

    View full-size slide

  101. validate :failure_message, presence: true, if: :failed?
    it "sometimes requires failure_message" do
    job = JobLog.new(status: :failed)
    expect(job).to validate_presence_if(:failure_message)
    job = JobLog.new(status: :completed)
    expect(job).not_to validate_presence_if(:failure_message)
    end

    View full-size slide

  102. DON’T BE AFRAID TO
    DELETE YOUR TESTS

    View full-size slide

  103. it "generates an array of strings” do
    expect(csv_data.length).to eq(shipments.length)
    expect(csv_data).to all(be_an(Array))
    end

    View full-size slide

  104. BEWARE OF
    OVER-MOCKING

    View full-size slide

  105. def user_summary(user)
    "(#{user.id}) #{user.full_name}, #{user.role}"
    end
    it "returns a user summary for printing, bad" do
    user = double(id: 5, full_name: "Dave G", role: :admin)
    response = user_summary(user)
    expect(response).to eq("(5) Dave G, admin")
    end

    View full-size slide

  106. it "returns a user summary for printing, better" do
    user = instance_double(User, id: 5,
    full_name: "Dave G",
    role: :admin)
    response = user_summary(user)
    expect(response).to eq("(5) Dave G, admin")
    end

    View full-size slide

  107. it "returns a user summary for printing, better" do
    user = User.new(id: 5,
    full_name: "Dave G",
    role: :admin)
    expect(user_summary(user)).to eq("(5) Dave G, admin")
    end

    View full-size slide

  108. 2. ACTUALLY, JUST DELETE THEM
    3. TINY PIGS ARE WAY CUTER
    THAN CATS
    1. KEEP YOUR TESTS MOIST

    View full-size slide

  109. 2. WRITE TESTS FOR HUMANS
    3. TINY PIGS ARE WAY CUTER
    THAN CATS
    1. USE TESTS TO DRIVE DESIGN

    View full-size slide