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

Your TDD Treasure Map

Your TDD Treasure Map

We know testing is vital and makes refactoring painless. But how to set sail to that TDD treasure? Yarr, we need to test to get experience, but need experience to test. Let’s draw a map with simple strategies for identifying test cases and building a robust test suite. X marks the spot w/ TDD tools for newbies and seasoned pirates alike.

Ajina Slater

May 25, 2022
Tweet

More Decks by Ajina Slater

Other Decks in Programming

Transcript

  1. doodlingdev
    Your


    TDD


    Tr
    ea
    sur
    e Map

    View full-size slide

  2. doodlingdev
    Aji
    they/them
    (all pr
    onoun
    s welcome)
    Hi! I mean.. ahoy! I'm Aji (hard J)

    I've been a professional developer since 2016, but if you count Geocities, I've been putting code on the web since 1996.

    Anyone in the room not yet born in 1996?

    I'm very excited you're here but now wish I had not asked that

    View full-size slide

  3. doodlingdev
    introductions
    I never know what to say in these intros.. so how about.. when I was in 4th grade I got the lead in the school play

    View full-size slide

  4. doodlingdev
    "Of Mice and Mozart" where we told the life story of Wolfgang Amadeus Mozart through the eyes of a family of mice that lived in his walls

    I played Mozart at 9 and I've been trying to relive that glory ever since

    View full-size slide

  5. doodlingdev
    let's begin
    I'm going to take a guess that we're on the same page about something, I'm going

    View full-size slide

  6. doodlingdev
    to assume that everyone here agrees that

    View full-size slide

  7. doodlingdev
    testing is important. And

    View full-size slide

  8. doodlingdev
    that Test Driven Development can be massively bene
    fi
    cial

    View full-size slide

  9. doodlingdev
    You're here at a software conference of a

    View full-size slide

  10. doodlingdev
    🤓
    community that overwhelmingly appreciates automated

    View full-size slide

  11. doodlingdev
    🤓🤓
    testing and you came to listen to "Your TDD Treasure

    View full-size slide

  12. doodlingdev
    🤓🤓🤓
    Map” which might just imply that TDD is a kind of treasure

    View full-size slide

  13. doodlingdev
    the benefits of tests
    (briefly)
    So I'm going to skip the arguments that try and convince you of the bene
    fi
    ts of robust automated tests that

    View full-size slide

  14. doodlingdev
    verify code
    verify your code is working properly

    View full-size slide

  15. doodlingdev
    defend against regression
    defend against regressions

    View full-size slide

  16. doodlingdev
    support refactoring
    support refactoring

    View full-size slide

  17. doodlingdev
    living documentation
    act as living documentation

    View full-size slide

  18. doodlingdev
    design guidance
    and provide design guidance.

    View full-size slide

  19. doodlingdev
    ( From Five Factor Testing
    By Sarah Mei )
    madeintandem.com/blog/five-factor-testing/
    And although I didn't understand the degree at the time, that was certainly the feeling they tried to instill in us at my coding bootcamp

    View full-size slide

  20. doodlingdev
    Please sir, I’d like some more tests..
    but we had so little time, and I graduated never having written a test, without feeling the bene
    fi
    ts of a good test suite...

    View full-size slide

  21. doodlingdev
    plus It seemed like a paradox. Write the thing that veri
    fi
    es the code I will have written BEFORE I am going to have written the code I will have been about to write.

    View full-size slide

  22. doodlingdev
    PFFFT okay, let me just jump into my time machine to take a look at the PR I'm going to put up tomorrow.

    View full-size slide

  23. doodlingdev
    Testing FIRST is di
    ff
    i
    cult. True for seasoned sailors, but even more so for those newer to this career when it's a challenge still to even reason about what code to write at
    all.

    View full-size slide

  24. doodlingdev
    How was I going to make it out of this mess? Like Archimedes in the bathtub or Newton and Gravity, we need a compelling yet apocryphal story of discovery to go with
    it.

    View full-size slide

  25. doodlingdev
    So.. scratching my head over this problem I wandered over to my bookshelf that has all my jobby job career type books.

    View full-size slide

  26. doodlingdev
    It holds Sandy Metz's Practical Object Oriented Design in Ruby

    View full-size slide

  27. doodlingdev
    and Atul Gawande's The Checklist Manifesto, wonderful tomes that have set me on the path when I needed them before...

    View full-size slide

  28. doodlingdev
    wait.. this is Robert Lewis Stevenson's Treasure Island. what? How did this end up on this shelf? so embarassing, the child of a librarian, misshelving happening in my
    own home.. But it dawned on me, of course it's here! because...

    View full-size slide

  29. doodlingdev
    treasure maps! Ok well..

    View full-size slide

  30. doodlingdev
    fl
    owcharts

    View full-size slide

  31. doodlingdev
    But think about it, a treasure map IS a
    fl
    owchart. Sure, one of the core stengths of a
    fl
    owchart is the branching, the logic, being able to map out decisions that lead to
    di
    ff
    erent outcomes

    but Stevenson's captain Flint only wants the happy path that ends in dubloons

    View full-size slide

  32. doodlingdev
    if we
    fi
    ll out the map

    View full-size slide

  33. doodlingdev
    put a decision branch here at the edge of this dense jungle

    View full-size slide

  34. doodlingdev
    🗺

    without the map in our hands, we're lost and 404 not found

    View full-size slide

  35. doodlingdev
    🗺

    one way lies treasure and the other to

    View full-size slide

  36. doodlingdev
    🗺


    an encampment of rival pirates and 409 con
    fl
    ict

    View full-size slide

  37. doodlingdev
    🗺


    a decision here

    View full-size slide

  38. doodlingdev
    🗺

    💰

    and EAST means treasure, but

    View full-size slide

  39. doodlingdev
    🗺

    💰

    🐊
    WEST means eaten by crocodiles, and just like that the whole crew is 410 gone

    View full-size slide

  40. doodlingdev
    🗺

    💰

    🐊
    Not only is a
    fl
    owchart useful for retracing your steps to your long-buried ill-begotten gains, but one of the most helpful artifacts for understanding the control
    fl
    ow of a
    system, invaluable documentation for new team members.

    View full-size slide

  41. doodlingdev
    A kind of visual pseudocode.. Think about how many low-code and no-code solutions are built with
    fl
    owcharts as a UI, because they're so simple to understand but
    potentially powerful.

    View full-size slide

  42. doodlingdev
    we can leverage that same strength as a shared language to build understanding between technical and non-technical team members and stakeholders and get buy-in
    from "the business" in a way you never can with code.

    View full-size slide

  43. doodlingdev
    But no longer will the usefulness of this tool stop at the code's edge, we're going to learn how to directly apply it to writing tests and eventually code...

    View full-size slide

  44. doodlingdev
    (pirate voice)

    So consider yerselves hornswaggled, ensnared inta' being my crew aboard the good ship Capybara, en route to the promised land where testin' before ye' leave harbor
    can be smooth saillin'. Before his half hour be over, we'll raid the ship o'knowledge and you'll have new techniques o' yer own te' boot

    View full-size slide

  45. doodlingdev
    I apologize for that accent to everyone watching who is a 17th century Carribean pirate, sorry.. privateer

    View full-size slide

  46. doodlingdev
    the process
    A chart can relate fairly directly to code, serving to mimic the control
    fl
    ow of an app.. but how it relates to testing might not be as obvious. We'll use a pretty common
    situation of a Rails controller action to demonstrate how we can take a handful of AC's to a
    fl
    owchart and turn that into a test rigging, test code, and application code.

    View full-size slide

  47. doodlingdev
    couple of quick caveats and de
    fi
    nitions to simplify our coming parlay

    View full-size slide

  48. doodlingdev
    feature
    when I say feature, i mean that to be any unit of work.. be it a whole feature, a bug
    fi
    x, partial implementation, whatever. I'll stick to saying "feature" for brevity.

    View full-size slide

  49. doodlingdev
    rigging
    There's probably an actual term for it, but when I'm talking about a test suite's "rigging" i mean the describe, context and it blocks that make up the tests.

    View full-size slide

  50. doodlingdev
    Rspec.describe PiratesController d
    o

    describe 'pirate/id' do
    context 'when logged in' d
    o

    it 'shows the pirate page' do
    like here, these lines with the descriptive strings, and the levels of indentation that show how they relate to one another, that's what I'm calling the "rigging"

    View full-size slide

  51. doodlingdev
    rspec
    although this process will work with any testing library... ruby or not, my examples are going to be in rspec, because as we all know it IS a pirate's

    View full-size slide

  52. doodlingdev
    arrrrr-spec
    favorite testing framework

    View full-size slide

  53. doodlingdev
    The app our team is building is Pirate Cove, where freelance freebooters can list their available skills and raiding resume for captains looking to round out a crew for
    upcoming misdeeds.. sorry.. adventures

    View full-size slide

  54. doodlingdev
    Pirates
    Users
    our app has users, we call them pirates, and pirates have pro
    fi
    le pages,

    o
    ff
    the shelf rails resource routing

    with the standard CRUD actions

    View full-size slide

  55. doodlingdev
    Pirates
    piratecove.app/pirates/:id
    our app has users, we call them pirates, and pirates have pro
    fi
    le pages,

    o
    ff
    the shelf rails resource routing

    with the standard CRUD actions

    View full-size slide

  56. doodlingdev
    Pirates
    Create, Read, Update, Destroy
    piratecove.app/pirates/:id
    our app has users, we call them pirates, and pirates have pro
    fi
    le pages,

    o
    ff
    the shelf rails resource routing

    with the standard CRUD actions

    View full-size slide

  57. doodlingdev
    crew
    mob programming
    This afternoon we're pairing, all of us, I'll drive

    we've picked up a ticket with a new user story

    View full-size slide

  58. doodlingdev
    As a pirate, I should be able to fill
    out the form on my profile page
    and submit it to change my
    information
    and it looks like it is to implement one of those CRUD actions, As a pirate, I should be able to
    fi
    ll out the form on my pro
    fi
    le page and submit it to change my information.

    View full-size slide

  59. doodlingdev
    Acceptance Criteria (ACs):


    1. User can update a pirate's
    information from the profile
    page
    (read slides) There are two eventual acceptance criteria or requirements on this ticket, but we're going to start with just the
    fi
    rst and pick up the second as we go.

    View full-size slide

  60. doodlingdev
    Begin with the action we know is going to take place, typically a user acting on the system

    here: Our pirate user submitting form data.

    View full-size slide

  61. doodlingdev
    Standard
    fl
    owchart symbols have start and end points as rounded shapes,

    We'll start with that core action at the top center of our workspace

    View full-size slide

  62. doodlingdev
    User submits
    form data
    when that gets submitted, an action occurs on the backend, we're going to save that new data

    View full-size slide

  63. doodlingdev
    User submits
    form data
    DB Save
    and how do we resolve? show the updated information with a redirect to :show

    View full-size slide

  64. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    this alone could be the happy path of our feature

    sure it's gonna get a little more complex

    but at least it's enough to write the matching rigging

    View full-size slide

  65. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Begins with a user action, some backend work, and feedback for the user. Beginning middle end

    View full-size slide

  66. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    # frozen_string_literal: true


    RSpec.describe PiratesController,


    type: :request do


    describe “patch#update” do
    (pause) at the top of an rspec
    fi
    le we get something like this to start, but everything we're doing will be under patch#update, this is the last time we'll see this to save the
    slides getting too cluttered

    View full-size slide

  67. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    right away I can see two outcomes that we expect will happen after form data is submitted.

    View full-size slide

  68. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    by rails convention: redirecting to the pirate show page

    and the only way the pirate show page is going to have the proper information

    View full-size slide

  69. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    is if we also update the db to re
    fl
    ect changed data

    View full-size slide

  70. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    This doesn't mean that every bubble in the
    fl
    owchart needs to be tested, and we'll see that coming up. In this case, we're testing more than just the very end, but also
    any action that changes something *outside* of our current test subject. A side e
    ff
    ect.

    View full-size slide

  71. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Our test subject is the PiratesController, and this makes a change in the database, way outside the controller. That's a result that we want to guarantee is happening (to
    the best of our ability) so we'll protect it with a test.

    View full-size slide

  72. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    That way, if anyone ever comes along and makes a change that a
    ff
    ects that outcome

    View full-size slide

  73. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”

    even if the redirect still happens

    View full-size slide

  74. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    💬
    thank


    you.
    we'll have feedback that an expected result has been a
    ff
    ected. That's our tests preventing regressions.

    View full-size slide

  75. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    (pause) The other piece to our test rigging are context blocks. The it blocks are going to match up to the END results, the e
    ff
    ects of the action

    View full-size slide

  76. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    the context will correlate to the setups that exist, those are found by following each individual path through the chart

    View full-size slide

  77. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    pretty easy at the moment, right now we've only got one path through the
    fl
    owchart

    View full-size slide

  78. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    it could make sense to wrap these two it blocks in a context block

    View full-size slide

  79. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    context “happy path” do
    end
    but i don't think we actually gain anything by doing that, it's the only context that exists, and it's already wrapped by the "patch update" describe block. Personal
    preference, but I'd leave it o
    ff
    for now.

    View full-size slide

  80. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    context “happy path” do
    end
    What else this indicates by being only one context, is that our setup for these two tests will be the same.

    View full-size slide

  81. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    👋
    Each individual path through the chart will have a di
    ff
    erent state when the action is performed. We're focused down to a level where there is only one action, and most of
    the time you’re building out tests, that's

    View full-size slide

  82. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    how it’s going to be. Maybe di
    ff
    erent granularities, sometimes di
    ff
    erent params, but one single insighting action.

    View full-size slide

  83. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    The only way to take different


    paths through the chart is with a
    different beginning state


    or different params of the action
    In the ideal state when you’re focued on testing one action, the only way to take di
    ff
    erent paths through the chart is with a di
    ff
    erent beginning state, or di
    ff
    erent params of
    the action.

    View full-size slide

  84. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    so we're still here, we kinda know that it's not going to stay this simple

    And this is a position you might
    fi
    nd yourself in often. You know there's more looming, but it hasn't actually shown up yet.

    View full-size slide

  85. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    let's not complicate it, I'd rather start moving forward on something I'm sure of, naive or not, rather than speculate and end up down an irrelevant rabbit hole.

    View full-size slide

  86. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    We've got some ambiguity, but we've also got something concrete, so let's move forward on that, I'm con
    fi
    dent enough that what comes next will reveal itself as we go
    through this exersize or that if it doesn't...

    View full-size slide

  87. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    it could be outside the scope of what we're trying to accomplish right now, plus I want to err on the side of shipping software over analysis paralysis

    View full-size slide

  88. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    and really, this is the whole happy path, right? nothing goes wrong, this is essentially what needs to happen.

    View full-size slide

  89. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    So if we write these tests, we'll have a north star to guide us as we sail along and seas get choppier. If ever we make changes, and THESE tests fail, we should do some
    rethinking.

    View full-size slide

  90. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Yup, our new tests will be preventing OUR regressions. Just another reminder that "the next developer" who will work with code we've written... can be ourselves, even
    just an hour later.

    View full-size slide

  91. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    So, should we write some tests?

    View full-size slide

  92. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    given


    when


    then
    the action
    the setup
    the result
    Wait, before that, I'm also not here to introduce or debate the di
    ff
    erent methodologies for writing individual tests, that's another talk, or workshop. And they mostly all boil
    down to some variation of GIVEN, WHEN, THEN.

    View full-size slide

  93. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    it “redirects to pirate :show” do
    end
    what might test code for this portion of our rigging look like?

    View full-size slide

  94. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })
    it “redirects to pirate :show” do
    end
    let's start with the setup, we know we need a pirate whose information is going to be updated, and whose ID will be part of the url, based on rails' restful conventions.

    View full-size slide

  95. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })
    it “redirects to pirate :show” do
    end
    expect do


    patch(pirate_url(test_pirate), params: {


    name: “Captain J. Flint”,


    position: “Captain”


    })


    end
    and now the action, we already said, that's going to be when the user submits the form data, so if we use rspec's request syntax, it might look something like this

    View full-size slide

  96. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })
    it “redirects to pirate :show” do
    end
    expect do


    patch(pirate_url(test_pirate), params: {


    name: “Captain J. Flint”,


    position: “Captain”


    })


    end
    .to redirect_to(pirate_url(pirate))
    and eventually our expectation, that it should redirect

    View full-size slide

  97. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })
    it “redirects to pirate :show” do
    end
    expect do


    patch(pirate_url(test_pirate), params: {


    name: “Captain J. Flint”,


    position: “Captain”


    })


    end
    .to redirect_to(pirate_url(pirate))
    it “updates db to reflect changed data” do


    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })


    patch(pirate_path(test_pirate), params: {


    name: “William Bones”,


    position: “Captain”


    })


    expect(Pirate.find(test_pirate.id).name)


    .to eq "William Bones"


    end
    Here we are with test code for both it blocks from before. Redirects to the user show page and updates the database.

    View full-size slide

  98. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })
    it “redirects to pirate :show” do
    end
    expect do


    patch(pirate_url(test_pirate), params: {


    name: “Captain J. Flint”,


    position: “Captain”


    })


    end
    .to redirect_to(pirate_url(pirate))
    it “updates db to reflect changed data” do


    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })


    patch(pirate_path(test_pirate), params: {


    name: “William Bones”,


    position: “Captain”


    })


    expect(Pirate.find(test_pirate.id).name)


    .to eq "William Bones"


    end
    Now we’ve got some wind in our sails! We’ve understood our
    fl
    owchart as a context and still at just the one path, our GIVEN is the matching setup. (Pause)

    View full-size slide

  99. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })
    it “redirects to pirate :show” do
    end
    expect do


    patch(pirate_url(test_pirate), params: {


    name: “Captain J. Flint”,


    position: “Captain”


    })


    end
    .to redirect_to(pirate_url(pirate))
    it “updates db to reflect changed data” do


    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })


    patch(pirate_path(test_pirate), params: {


    name: “William Bones”,


    position: “Captain”


    })


    expect(Pirate.find(test_pirate.id).name)


    .to eq "William Bones"


    end
    the action that begins our
    fl
    owchart has become the WHEN: the action taken in our test cases.

    View full-size slide

  100. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })
    it “redirects to pirate :show” do
    end
    expect do


    patch(pirate_url(test_pirate), params: {


    name: “Captain J. Flint”,


    position: “Captain”


    })


    end
    .to redirect_to(pirate_url(pirate))
    it “updates db to reflect changed data” do


    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })


    patch(pirate_path(test_pirate), params: {


    name: “William Bones”,


    position: “Captain”


    })


    expect(Pirate.find(test_pirate.id).name)


    .to eq "William Bones"


    end
    We’ve identi
    fi
    ed the two end states, one for the http response, and one for the database

    View full-size slide

  101. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })
    it “redirects to pirate :show” do
    end
    expect do


    patch(pirate_url(test_pirate), params: {


    name: “Captain J. Flint”,


    position: “Captain”


    })


    end
    .to redirect_to(pirate_url(pirate))
    it “updates db to reflect changed data” do


    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })


    patch(pirate_path(test_pirate), params: {


    name: “William Bones”,


    position: “Captain”


    })


    expect(Pirate.find(test_pirate.id).name)


    .to eq "William Bones"


    end
    And we’ve captured them as our THEN in expectations

    View full-size slide

  102. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })
    it “redirects to pirate :show” do
    end
    expect do


    patch(pirate_url(test_pirate), params: {


    name: “Captain J. Flint”,


    position: “Captain”


    })


    end
    .to redirect_to(pirate_url(pirate))
    it “updates db to reflect changed data” do


    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })


    patch(pirate_path(test_pirate), params: {


    name: “William Bones”,


    position: “Captain”


    })


    expect(Pirate.find(test_pirate.id).name)


    .to eq "William Bones"


    end
    (pause) As I'm building out a system, I'm trying to uncover moments where we might be taking something for granted. An assumption that we've built on top of that isn't
    as guaranteed as we're treating it

    View full-size slide

  103. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })
    it “redirects to pirate :show” do
    end
    expect do


    patch(pirate_url(test_pirate), params: {


    name: “Captain J. Flint”,


    position: “Captain”


    })


    end
    .to redirect_to(pirate_url(pirate))
    it “updates db to reflect changed data” do


    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })


    patch(pirate_path(test_pirate), params: {


    name: “William Bones”,


    position: “Captain”


    })


    expect(Pirate.find(test_pirate.id).name)


    .to eq "William Bones"


    end
    So I'm asking myself, 'what here is _built on a lie_?' what here could go wrong? and my eye is drawn to this.

    View full-size slide

  104. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })
    it “redirects to pirate :show” do
    end
    expect do


    patch(pirate_url(test_pirate), params: {


    name: “Captain J. Flint”,


    position: “Captain”


    })


    end
    .to redirect_to(pirate_url(pirate))
    it “updates db to reflect changed data” do


    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })


    patch(pirate_path(test_pirate), params: {


    name: “William Bones”,


    position: “Captain”


    })


    expect(Pirate.find(test_pirate.id).name)


    .to eq "William Bones"


    end
    These params are sent in by a user. You know users, and so do I...and I don't trust 'em. They will
    fi
    nd ..interesting ways to stress a system. Brand new ways to use our
    software that we never dreeeeamed of.

    View full-size slide

  105. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })
    it “redirects to pirate :show” do
    end
    expect do


    patch(pirate_url(test_pirate), params: {


    name: “Captain J. Flint”,


    position: “Captain”


    })


    end
    .to redirect_to(pirate_url(pirate))
    it “updates db to reflect changed data” do


    test_pirate = Pirate.create({


    name: "Billy Bones",


    position: "First Mate"


    })


    patch(pirate_path(test_pirate), params: {


    name: “William Bones”,


    position: “Captain”


    })


    expect(Pirate.find(test_pirate.id).name)


    .to eq "William Bones"


    end
    In reality, that's a gift, it really is and I believe that. But right now.. it's a problem. So far our tests are assuming everything is going to go smoothly.

    We need to handle what happens when patch params don't pass validation.

    View full-size slide

  106. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Let's fold up the test code, like we're in our editor, and add this params protection. What would params that don't pass validation change about the
    fl
    ow of the chart?

    View full-size slide

  107. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Yesss! That change will never make it to the database. And because that can fail, we need to add to the
    fl
    owchart, before the persistence, a check for validity.

    View full-size slide

  108. doodlingdev
    User submits
    form data
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    DB Save
    Display
    profile
    We notate this fork in the road with a diamond Because in Flowchartland, which is a province of Diagramopolous, a diamond represents a decision. Something in the
    setup or the action is a
    ff
    ecting the outcome

    View full-size slide

  109. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    the value of which decides the branch of the
    fl
    ow to continue down, and now we truly have more than one path through the feature, and with it another context that
    needs testing.

    View full-size slide

  110. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    let's trace the two paths so we can see what we're dealing with

    View full-size slide

  111. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    if a path is a context, we're able to tell that one context is separate from the other. the nodes in the
    fl
    owchart that are re
    fl
    ected in the rigging as our ...

    View full-size slide

  112. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    in the rigging as our ...existing specs cannot be reached by the new path, they are an entirely di
    ff
    erent context

    View full-size slide

  113. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    let's re
    fl
    ect that in the rigging. (pause) But how can we know what the context involves, it's not as simple as an endpoint.. there isn't one node in the
    fl
    owchart that
    equates to context, but many along a path.

    View full-size slide

  114. doodlingdev
    it “redirects to pirate :show”
    it “updates the db with changed data”
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Let's ask ourselves a question, what given, what setup or input will cause the decision to go to the right where we see the two it blocks we already have?

    View full-size slide

  115. doodlingdev
    it “redirects to pirate :show”
    it “updates the db with changed data”
    context "with valid params" do
    end
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    valid params, right?

    View full-size slide

  116. doodlingdev
    it “redirects to pirate :show”
    it “updates the db with changed data”
    context "with valid params" do
    end
    context "with invalid params" do
    end
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    and then the opposite, invalid params, causes the
    fl
    ow to the left.

    View full-size slide

  117. doodlingdev
    it “redirects to pirate :show”
    it “updates the db with changed data”
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    and the it block, based on rails conventions we'll render the edit page again and send the object over to the view with those validation errors attached

    View full-size slide

  118. doodlingdev
    it “redirects to pirate :show”
    it “updates the db with changed data”
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Let's act on these tests and
    fl
    owchart that we have currently. what do we want to do next? Personally.. I'm not going to feel good about these tests unless I see them fail.
    We haven't written any code for them yet, so if they pass, there is something
    fi
    shy going on.

    View full-size slide

  119. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    it “redirects to pirate :show”
    it “updates the db with changed data”
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    Finished in 0.0819 seconds (files took 5.08 seconds to load)
    3 examples, 3 failures
    Great news! the tests failed. That means we can write some code to make them pass.

    View full-size slide

  120. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    it “redirects to pirate :show”
    it “updates the db with changed data”
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    def update
    end
    We're going to keep this as simple as possible, but let's think about the code that we'll need in order to follow the
    fl
    owchart and satisfy the tests.

    View full-size slide

  121. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    it “redirects to pirate :show”
    it “updates the db with changed data”
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    def update
    end
    @pirate = Pirate.find(params[:id])
    we'll need to know which pirate

    View full-size slide

  122. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    it “redirects to pirate :show”
    it “updates the db with changed data”
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    def update
    end
    @pirate = Pirate.find(params[:id])
    if @pirate.update(pirate_params)


    redirect_to pirate_url(@pirate)
    if the update works, we'll redirect

    View full-size slide

  123. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    it “redirects to pirate :show”
    it “updates the db with changed data”
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    def update
    end
    @pirate = Pirate.find(params[:id])
    if @pirate.update(pirate_params)


    redirect_to pirate_url(@pirate)
    else


    render :edit


    end
    and if not, render edit. I didn't do anything fancy here, this is direct from the rails generators, condensed for slide readability. how do our tests fare?

    View full-size slide

  124. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    it “redirects to pirate :show”
    it “updates the db with changed data”
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    Finished in 0.1219 seconds (files took 5.08 seconds to load)
    3 examples, 0 failures
    bingo! great work every body.

    View full-size slide

  125. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    Let's take just a moment to re
    fl
    ect on how we got here, we used the ticket requirements to make a simple
    fl
    owchart that met the user story, the happy path

    View full-size slide

  126. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    and from that
    fl
    owchart came our test rigging

    View full-size slide

  127. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    We wrote some tests and uncovered a failure state

    View full-size slide

  128. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    which led us to expand on our
    fl
    owchart

    View full-size slide

  129. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    and rigging

    View full-size slide

  130. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    From there we used our understanding of the system at play to write the application code

    View full-size slide

  131. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    chart , rigging , test , chart , rigging , code

    View full-size slide

  132. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    All that is to say, this process isn't going to be linear most of the time.

    View full-size slide

  133. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    Usually we won't have written the whole entire
    fl
    owchart with everything in scope, then move on to the entire rigging, then the test code, then the app code.

    View full-size slide

  134. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    We'll bounce around, acting on the next bit of information that we're con
    fi
    dent about, uncovering assumptions as we go, and adding to our framework as we progress.

    View full-size slide

  135. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    it “redirects to pirate :show”
    it “updates the db with changed data”
    Valid?
    Show form


    with errors
    yes
    no
    context "with valid params" do
    end
    context "with invalid params" do
    end
    it "renders :edit"
    well.. we don't really have everything that's in scope, do we? remember, there's that second acceptance criteria.

    View full-size slide

  136. doodlingdev
    Acceptance Criteria:


    1. Can update a pirate's profile info
    2. Only authorized pirates can
    perform updates
    1 we already have, can update a pirate's pro
    fi
    le info, but 2 here says that only authorized pirates can perform updates.

    View full-size slide

  137. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    so before our check for validity, in the same way that we added the valid? step before persistence

    View full-size slide

  138. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    we have a new step that can interrupt the process before reaching the end states we had charted before

    View full-size slide

  139. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    which leads to another path through the chart.

    ignore for now the implementation of even having a logged in user, I've only got ( x ) more minutes with you.

    View full-size slide

  140. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    another path should make us think what? another context

    View full-size slide

  141. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    using the technique we did before, we can wrap the expectations based on when their paths diverge.

    The
    fi
    rst time any paths diverge, they go in two di
    ff
    erent directions. Green to the left, blue and orange to the right.

    View full-size slide

  142. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    That tells us there will be two contexts, and it's the decision where they branch that tells us what the context is.

    View full-size slide

  143. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    context "pirate is unauthorized" do


    it "returns 401: Unauthorized"


    end
    Authorized, no: that's green.

    View full-size slide

  144. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    context "pirate is unauthorized" do


    it "returns 401: Unauthorized"


    end
    and because the other paths share the state of a pirate that has permissions to change this data

    View full-size slide

  145. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    context "pirate is unauthorized" do


    it "returns 401: Unauthorized"


    end
    context "pirate is authorized" do
    end
    the context about being authorized will wrap them both.

    View full-size slide

  146. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    context "pirate is unauthorized" do


    it "returns 401: Unauthorized"


    end
    context "pirate is authorized" do
    end
    But we've done this kind of exercise before.. why bring it up again? because I want to throw a metaphorical match in our powder keg.

    View full-size slide

  147. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    context "pirate is unauthorized" do


    it "returns 401: Unauthorized"


    end
    context "pirate is authorized" do
    end
    Turns out what makes someone authorized... isn't so cut and dry. Authorized yes/no is perfectly
    fi
    ne for the
    fl
    owchart here... because at the controller level, that's the
    fl
    ow
    of this action.

    View full-size slide

  148. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    context "pirate is unauthorized" do


    it "returns 401: Unauthorized"


    end
    context "pirate is authorized" do
    end
    but to be authorized.. the logged in pirate is EITHER the pirate being changed OR a pirate with the admin role

    View full-size slide

  149. doodlingdev
    User submits
    form data
    DB Save
    Display profile
    Valid?
    Show form


    with errors
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    context "pirate is unauthorized" do


    it "returns 401: Unauthorized"


    end
    context "pirate is authorized" do
    end
    Same Pirate?
    no yes
    no
    yes
    Admin?
    what was once 3 paths through the chart is now
    fi
    ve, if we re
    fl
    ect that in the test cases we go from 4

    View full-size slide

  150. doodlingdev
    User submits
    form data
    DB Save
    Display profile
    Valid?
    Show form


    with errors
    yes
    no
    401
    Same Pirate?
    no yes
    no
    yes
    Admin?
    context "logged in user is the same user" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user is admin" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user not admin or user" do


    it “sends 401”


    end
    to 7. that's 57% more tests.. and I like tests and all, but conceptually this doesn't feel too much di
    ff
    erent than what we already had

    View full-size slide

  151. doodlingdev
    User submits
    form data
    DB Save
    Display profile
    Valid?
    Show form


    with errors
    yes
    no
    401
    Same Pirate?
    no yes
    no
    yes
    Admin?
    context "logged in user is the same user" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user is admin" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user not admin or user" do


    it “sends 401”


    end
    We added this section, but it makes the same decision: authorized yes or no.. it's just a more complicated process.

    Maybe you're already standin' where i'm leading you.. or maybe you're on the way..

    View full-size slide

  152. doodlingdev
    User submits
    form data
    DB Save
    Display profile
    Valid?
    Show form


    with errors
    yes
    no
    401
    context "logged in user is the same user" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user is admin" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user not admin or user" do


    it “sends 401”


    end
    Same Pirate?
    Admin?
    no yes
    no
    yes
    if this box was opaque...

    View full-size slide

  153. doodlingdev
    User submits
    form data
    DB Save
    Display profile
    Valid?
    Show form


    with errors
    yes
    no
    401
    Same Pirate?
    no yes
    no
    yes
    Admin?
    context "logged in user is the same user" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user is admin" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user not admin or user" do


    it “sends 401”


    end
    Authorized?
    and all we saw was authorized yes/no.. the rest of the paths would have the same information and the same
    fl
    ow, right?

    View full-size slide

  154. doodlingdev
    context "logged in user is the same user" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user is admin" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user not admin or user" do


    it “sends 401”


    end
    User submits
    form data
    DB Save
    Display profile
    Valid?
    Show form


    with errors
    yes
    no
    401
    Same Pirate?
    no yes
    no
    yes
    Admin?
    Authorized?
    Does the PiratesController need to know how to tell if a pirate can make the change? or does it just need to know whether to make 'em walk the plank or not? the latter,
    right?

    View full-size slide

  155. doodlingdev
    User submits
    form data
    DB Save
    Display profile
    Valid?
    Show form


    with errors
    yes
    no
    401
    context "logged in user is the same user" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user is admin" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user not admin or user" do


    it “sends 401”


    end
    Same Pirate?
    Same Pirate?
    no yes
    no
    yes
    Authorized?
    Call with pirate


    and editing pirate
    Same Pirate?
    Admin?
    no yes
    no
    yes
    Unauthorized Authorized
    so let's encapsulate that logic in an object. Lookit this, robert lewis stevenson and standy metz, back together after all this time.

    View full-size slide

  156. doodlingdev
    User submits
    form data
    DB Save
    Display profile
    Valid?
    Show form


    with errors
    yes
    no
    401
    context "logged in user is the same user" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user is admin" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user not admin or user" do


    it “sends 401”


    end
    Same Pirate?
    Same Pirate?
    no yes
    no
    yes
    Authorized?
    Call with pirate


    and editing pirate
    Same Pirate?
    Admin?
    no yes
    no
    yes
    Unauthorized Authorized
    and because we can test the authorization logic

    over in this object's tests... (some might even call it a service object) it doesn't have to be a part of our controller test at all.

    View full-size slide

  157. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    User submits
    form data
    DB Save
    Display profile
    Valid?
    Show form


    with errors
    yes
    no
    401
    context "logged in user is the same user" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user is admin" do


    context “with valid params” do


    it “redirects to user show page”


    it “updates the db to reflect changed data”


    end


    context “with invalid params” do


    it “sends error message”


    end


    end


    context "logged in user not admin or user" do


    it “sends 401”


    end
    Same Pirate?
    Same Pirate?
    no yes
    no
    yes
    Authorized?
    Call with pirate


    and editing pirate
    Same Pirate?
    Admin?
    no yes
    no
    yes
    Unauthorized Authorized
    We can mock the call to the service object, within the test.. force it to say yes or no.. all part of our setup and suddenly we're back!

    View full-size slide

  158. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    context "pirate is unauthorized" do


    it "returns 401: Unauthorized"


    end
    context "pirate is authorized" do
    end
    with just authorized yes/no. And the next time you're looking to encapsulate some logic away in a service object...

    View full-size slide

  159. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    context "pirate is unauthorized" do


    it "returns 401: Unauthorized"


    end
    context "pirate is authorized" do
    end
    look for parts of your
    fl
    owchart where those paths go apart, do something self contained, but then converge back together. like we did here.

    View full-size slide

  160. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    context "pirate is unauthorized" do


    it "returns 401: Unauthorized"


    end
    context "pirate is authorized" do
    end
    but if we think about it, we already have similar nodes in this
    fl
    owchart, don't we?

    View full-size slide

  161. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    context "pirate is unauthorized" do


    it "returns 401: Unauthorized"


    end
    context "pirate is authorized" do
    end
    yes! right here! this isn't a single thing.. it's active record taking our params hash, doing --something-- with Arel, spitting out SQL, traversing to a database (a whole
    di
    ff
    erent program), updating a resource.. imagine the
    fl
    owchart on that!

    View full-size slide

  162. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    context "pirate is unauthorized" do


    it "returns 401: Unauthorized"


    end
    context "pirate is authorized" do
    end
    and Active Record just handles it all so we can put a single box in our
    fl
    owchart.

    View full-size slide

  163. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    context "pirate is unauthorized" do


    it "returns 401: Unauthorized"


    end
    context "pirate is authorized" do
    end
    In the same way that we zoomed IN to get to an authorization service class, and we understand the zooming behind the persist node, we can also zoom out from our
    controller.

    View full-size slide

  164. doodlingdev
    User submits
    form data
    DB Save
    Display
    profile
    Valid?
    Show form


    with errors
    yes
    no
    Authorized?
    yes
    no
    401
    context "with valid params" do


    it "redirects to pirate :show"


    it "updates the db with changed data"


    end


    context "with invalid params" do


    it "renders :edit"


    end
    context "pirate is unauthorized" do


    it "returns 401: Unauthorized"


    end
    context "pirate is authorized" do
    end
    Up until this point we've been looking at a single action...and everything else happens without our in
    fl
    uence.

    View full-size slide

  165. doodlingdev
    this is a
    fl
    owchart for ordering a book from amazon. this is very high level. really zoomed out, all the way to the user. evvvverything is abstracted away, they're not
    thinking about html, servers, databases, none of it.

    and yet this is an incredibly useful diagram, even for testing, even for writing code

    View full-size slide

  166. doodlingdev
    in the same way that our happy path tests were our north star while building and refactoring, something like this can be the north star for an entire business or
    development team, able to ask themselves "can a user still order a book?" or "does this help a user order a book?". probably not amazon anymore because nowhere on
    here do I see "shoot founder into space". I don't see "let him come back" either but we did.

    View full-size slide

  167. doodlingdev
    each of these purple blocks is an action by a user. sign in.. add to cart.. each and every one of them could be a controller action, each with it's own
    fl
    owchart as intricate
    (or moreso) than the one we just built. but at this level, this "can a user order a book" level, those aren't important, for the same reason that it wasn't important to our
    controller if the user was an admin or a speci
    fi
    c pirate.

    View full-size slide

  168. doodlingdev
    still each branch or each
    fl
    ow is going to need a test, maybe at this level a context maybe not, again.. personal preference. but i see at least..5? 6? tests here? eventually
    every purple box reached by the path of a test from "start" to "end"

    View full-size slide

  169. doodlingdev
    i hope that helps you see that these aren't "rules" on how to do the Treasure Map "technique", but some ideas and advice on how you might be able to apply it in your
    work. I literally do this in my work.

    View full-size slide

  170. doodlingdev
    This is a commit message I wrote just last week. That's right.. I put the
    fl
    owchart IN TO The commit message. And if you too want your commit messages to bring all the
    boys to the yard

    View full-size slide

  171. doodlingdev
    the tool I used to make this plaintext
    fl
    owchart is ascii
    fl
    ow at ascii
    fl
    ow.com. So yeah, take these ideas, use 'em, throw away what isn't working, add on where it falls
    short.

    View full-size slide

  172. doodlingdev
    OH and if you DO add on or do something cool with it, please please tell me, on the twits, the gram, github, any of the things.

    View full-size slide

  173. doodlingdev
    I would love to know how you remix this... or adapt it to other frameworks and languages because

    View full-size slide

  174. doodlingdev
    Arr, We might be here under the auspices of land lubbin' RAILS rather than ocean waves, I can without OBJECTIVE-C whether yer features be crafted o' RUBY,
    EMERALD, carved of ELM, adorned with JADE and PERL or crusted in RUST, arrrrrrrR, this ELIXIR
    fl
    ies truthy as a DART for all who sail the C from JAVA to CEYLON.

    In LUA more formal errrrr..LANG-uage, and you might consider it BASIC, but even through a COMMON LISP I wont be hearin' any SMALLTALK, be'cuz this ship be
    SWIFT as an OCAML crossing the mighty GO ...bi desert.

    But to give this here GROOVY ASSEMBLY some UNITY and CLOJURE, let me be CRYSTAL clear with ye, ALGOL to Davy Jones' Locker and BASH a hundred foot
    PYTHON before I give up me Test Driven Development.

    And now that you've seen the AMPL SCAL-A this technique,

    I hope yer acceptance criteria ever be clear,

    yer test suite not scuttle yer deployment,

    and the cloud never take the wind out of yer sails,

    so that the good ship CAPYBARA can see you home.

    View full-size slide

  175. doodlingdev
    madeintandem.com/blog/five-factor-testing/
    "Five Factor Testing" - Sarah Mei
    asciiflow asciiflow.com

    View full-size slide