$30 off During Our Annual Pro Sale. View Details »

Seattle.rb - Refactoring & BDD Presentation

Seattle.rb - Refactoring & BDD Presentation

A presentation on code refactoring that I gave at Seattle.rb in October, 2012. There is also a behavioral driven development example from my work on the CapGun http://capgun.io/ service.

Mike Mondragon

October 09, 2012
Tweet

More Decks by Mike Mondragon

Other Decks in Programming

Transcript

  1. Refactoring
    Mike Mondragon
    @monde
    Tuesday, January 8, 13

    View Slide

  2. Thank you Seattle.rb
    Tuesday, January 8, 13

    View Slide

  3. Refactoring?
    Tuesday, January 8, 13

    View Slide

  4. “Refactoring”
    Tuesday, January 8, 13

    View Slide

  5. “Refactoring”
    •Examples
    •Definition & Principles
    •Symptoms (Code Smells)
    •Testing
    •Catalog
    Tuesday, January 8, 13

    View Slide

  6. Why Refactor?
    •My code sucks
    •I don’t know what I’m doing
    •I don’t know what this code
    is doing
    Tuesday, January 8, 13

    View Slide

  7. Why Refactor?
    •Their code sucks
    •They don’t know what they
    are doing
    •I don’t know what their
    code is doing
    Tuesday, January 8, 13

    View Slide

  8. The less your problem is
    understood, the more
    complicated your solution is
    likely to be.
    Somebody Said This
    Tuesday, January 8, 13

    View Slide

  9. Refactoring
    •Modify code in small steps
    •Write tests for code that is
    affected
    •Write human readable code
    Tuesday, January 8, 13

    View Slide

  10. package.data.size == 0
    package.data.size < 12
    package.data.size == 12
    Example
    Tuesday, January 8, 13

    View Slide

  11. Example
    Logic defining the state of the
    object is contained outside of it
    package.data.size == 0
    package.data.size < 12
    package.data.size == 12
    Tuesday, January 8, 13

    View Slide

  12. package.data.size == 12 ||
    package.data.size == 13
    Example
    • Hard to find all places of code
    implementing outside logic
    • Object doesn’t control it’s own logical
    state
    Tuesday, January 8, 13

    View Slide

  13. Cat Picture
    -- (DO NOT WANT)
    Tuesday, January 8, 13

    View Slide

  14. Query? Method Pattern
    •Object tells you it’s state
    •Method is intention revealing
    •Is human readable
    •Examples: obj.nil? ... num.zero?
    Tuesday, January 8, 13

    View Slide

  15. Example
    Write tests first
    it "can be empty" do
    @package.empty?.must_equal true
    end
    it "can't be partial" do
    @package.partial?.must_equal false
    end
    it "can't be complete" do
    @package.complete?.must_equal false
    end
    Tuesday, January 8, 13

    View Slide

  16. Example
    Fail is good
    1) Error:
    test_0001_can_be_empty(Package):
    NoMethodError: undefined method `empty?' for #
    spec.rb:16:in `block (2 levels) in '
    2) Error:
    test_0002_can_t_be_partial(Package):
    NoMethodError: undefined method `partial?' for #
    spec.rb:20:in `block (2 levels) in '
    3) Error:
    test_0003_can_t_be_complete(Package):
    NoMethodError: undefined method `complete?' for #0x007fc304d93f18>
    spec.rb:24:in `block (2 levels) in '
    Tuesday, January 8, 13

    View Slide

  17. Example
    Implementation, State Is In A Single Location
    def empty?
    data.size.zero?
    end
    def partial?
    !empty? && data.size < 12
    end
    def complete?
    data.size == 12 || data.size == 13
    end
    Tuesday, January 8, 13

    View Slide

  18. I heard you like state so
    much, I put some ...
    Tuesday, January 8, 13

    View Slide

  19. Example
    Passing but ...
    # Running tests:
    Package#test_0002_can_t_be_partial = 0.49 s = .
    Package#test_0001_can_be_empty = 0.54 s = .
    Package#test_0003_can_t_be_complete = 0.98 s = .
    Finished tests in 2.007646s, 1.4943 tests/s, 1.4943 assertions/s.
    3 tests, 3 assertions, 0 failures, 0 errors, 0 skips
    wtf? 2 seconds
    Tuesday, January 8, 13

    View Slide

  20. Example
    Production data hardcoded into implementation :(
    def data
    JSON.parse(HTTParty.get(
    'http://goo.gl/4L1Zd'
    ))
    end
    Tuesday, January 8, 13

    View Slide

  21. Example
    Mock or override #data method in specs
    module SpecEmpty
    def data
    []
    end
    end
    Tuesday, January 8, 13

    View Slide

  22. Example
    Mock or override #data method in
    specs - clears away distractions
    before do
    @package = Package.new
    @package.extend(SpecEmpty)
    end
    it "can be empty" do
    @package.empty?.must_equal true
    end
    Tuesday, January 8, 13

    View Slide

  23. Example
    Passing and ...
    # Running tests:
    Package::empty#test_0002_can_t_be_partial = 0.00 s = .
    Package::empty#test_0001_can_be_empty = 0.00 s = .
    Package::empty#test_0003_can_t_be_complete = 0.00 s = .
    Finished tests in 0.000742s, 4043.1267 tests/s, 4043.1267 assertions/s.
    3 tests, 3 assertions, 0 failures, 0 errors, 0 skips
    fast! ~0 seconds
    Tuesday, January 8, 13

    View Slide

  24. Simple Mistakes Compound
    Upon Themselves
    • logic was external to object
    • external logic is hard to maintain/find
    • we had no tests
    • external data implementation had to be
    addressed in testing
    Tuesday, January 8, 13

    View Slide

  25. Pro-Tips
    • Refactoring Favors Behavior Rather Than
    Logic
    • Writing code that can be unit tested
    changes your writing style
    • If a method can’t be unit tested it must be
    refactored
    • Factor out interaction with frameworks
    from code that is tested
    Tuesday, January 8, 13

    View Slide

  26. Cat
    Picture
    Tuesday, January 8, 13

    View Slide

  27. Outside-In
    Let your tests drive your implementation
    Tuesday, January 8, 13

    View Slide

  28. Green Field Project
    Tuesday, January 8, 13

    View Slide

  29. Green Field Project
    Tuesday, January 8, 13

    View Slide

  30. This green field is already in
    production
    Tuesday, January 8, 13

    View Slide

  31. Green Field Project
    Tuesday, January 8, 13

    View Slide

  32. http://capgun.io/
    Tuesday, January 8, 13

    View Slide

  33. capgun.io
    API for web thumbs and web screens using
    headless webkit via PhantomJS/CasperJS
    Tuesday, January 8, 13

    View Slide

  34. CapGun Started Here
    Tuesday, January 8, 13

    View Slide

  35. So MANY Choices!
    • Sessions
    • Authentication
    • JSON rendering
    • Data Store(s)
    • ORM
    • framework(s)
    Tuesday, January 8, 13

    View Slide

  36. Start Somewhere
    Feature: Order Estimates
    As an API client
    In order to list information about orders
    I want to be able to query the order API
    Background:
    Given I am a valid API user
    And I send and accept JSON
    Scenario: getting an estimate for a base screen capping job
    When I send a POST request to "/api/v1/orders/estimate" with the following body:
    |{ "url": "http://example.com/cucumber#test" }|
    Then the response should be "200"
    And the JSON response should not have "order/job"
    And the JSON response should have an estimate of 1 credits
    Tuesday, January 8, 13

    View Slide

  37. Start Somewhere
    describe "Estimate" do
    describe "json" do
    should "estimate capping a URL for 1 credit" do
    post '/estimate.json',
    '{"url":"http://www.seattlerb.org/"}'
    JSON.parse(last_response.body).should == {
    'order' {
    'id' '4fd20a1288f560177600000a'
    'url' 'http://www.seattle.rb/'
    'cost' 1
    }
    }
    end
    end
    end
    Tuesday, January 8, 13

    View Slide

  38. Write Code That Is Used
    Immediately, Do Not Future Proof It
    class Order
    def cost
    end
    end
    describe Order do
    describe "defaults" do
    it "should cost 1 credit" do
    subject.cost.should == 1
    end
    end
    end
    Tuesday, January 8, 13

    View Slide

  39. Dog Food ASAP!
    Tuesday, January 8, 13

    View Slide

  40. Dog Food ASAP!
    http://tumblr.capgun.io/
    Tuesday, January 8, 13

    View Slide

  41. • Write Specs
    • Fail
    • Write Code
    • Pass
    • ????
    • Profit
    Iterate
    Tuesday, January 8, 13

    View Slide

  42. Outside-In / BDD is
    really just “poor man’s
    pair programming” *TM
    Tuesday, January 8, 13

    View Slide

  43. Thank You!
    Tuesday, January 8, 13

    View Slide

  44. Mike Mondragon
    •http://plasti.cx/
    •@monde Twitter
    •@monde Github
    •@monde coderwall
    Tuesday, January 8, 13

    View Slide

  45. Image Attribution
    @monde Gavatar by Mike Mondragon
    https://secure.gravatar.com/avatar/74c036cfbef785b139d06ccad4687b44?s=420&d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png
    Phil Hagelberg Seattle.rb shirt by veganstraightedge
    http://www.flickr.com/photos/veganstraightedge/7843002706/in/set-72157631203475428
    Amazon cover of “Refactoring” book
    http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672
    Wallace the cat cradled like a baby - Mike Mondragon
    Xzibit head shot - The Internet
    Wallace - THE INTERNETS is watching - Mike Mondragon on icanhazcheezburger
    Green field @ wallpaperstock.net
    http://wallpaperstock.net/green-field_wallpapers_8739_1024x768_1.html
    Palouse wheat field - unknown
    Palouse wheat field - unknown
    Tilled field - unknown
    CapGun logo - Mike Mondragon
    Skitch screen cap of CapGun panel - Mike Mondragon
    Tilled field - unknown
    Kitten asleep in food dish
    http://catlovingcare.com/2042/blog/loosing-weight-tips-for-fat-cats/2009/12/
    Skitch screen cap of http://tublr.capgun.io/ - Mike Mondragon
    CapGun.io result for Seattlerb.org - Mike Mondragon
    Wallace the cat profile - Mike Mondragon
    Tuesday, January 8, 13

    View Slide