• Home

  • Custom Ecommerce
  • Application Development
  • Database Consulting
  • Cloud Hosting
  • Systems Integration
  • Legacy Business Systems
  • Security & Compliance
  • GIS

  • Expertise

  • About Us
  • Our Team
  • Clients
  • Blog
  • Careers

  • CasePointer

  • VisionPort

  • Contact
  • Our Blog

    Ongoing observations by End Point Dev people

    Feature Isolation with Mike Farmer

    Brian Buchalter

    By Brian Buchalter
    October 9, 2012

    My brilliant co-worker Mike Farmer gave a presentation today talking about a development strategy he calls “Feature Isolation.” It involves developing new features on the fringe of your application, isolating it from the complexity of existing code. This allows greater focus on ensuring that your feature is well designed from an object-oriented perspective and that you don’t build more than you need.

    In order to truly isolate the feature, Mike put together some cucumber tools to allow you to run cucumber without Rails and to create what he calls a “FastModel”. The models are fast for two reasons. First, you don’t need to load ActiveRecord to get functionality like specifying field names, specifying relationships, or emulating saving records. Second, it let’s you to sketch out a design for your class while the cost of change is very very low.

    An Example: Product Variants

    Here’s an example of a tight little feature and step set for showing shoppers a comparison of product variants.

    Feature: As a shopper, I want to compare the variants of a product
    
      Background:
        Given there is a product named "Product A"
        And it has some variants with various options
        And I am on the comparison page for "Product A"
    
      Scenario: The shopper sees a comparison chart of Variant A
        When "Variant A" has options "a,b,c"
        Then the comparison chart header should have options "a,b,c"
        And the comparison grid should have 3 checkboxes
    

    When a feature request comes in you write out your cucumber scenarios and do the usual sign-off from the product manager. Nothing new or exciting here. Next, implement failing step files; yes of course they should fail first, this is TDD right? Mike has a strong opinion about what your features and steps should look like: short and specific. He’s found that if you’re writing too much setup, your feature is not well isolated or that perhaps this feature needs to be broken into several smaller ones.

    Now that we have the feature in place, we can create the steps.

    Given /^there is a product named "([^"]*)"$/ do |product_name|
        @product = Product.create(:name => product_name)
      end
    
      Given /^it has some variants with various options$/ do
        @variant_a = Variant.create(:sku => 'Variant A', product => @product)
        VariantOption.create(:name => "Option A", :variant => @variant_a)
        #... same thing for B, C, D
      end
    
      Given /^I am on the comparison page for "([^"]*)"$/ do |product|
        # UI Action
        # visit product_path(@product.permalink)
        @current_product = Product.find product
      end
    
      When /^"([^"]*)" has options "([^"]*)"$/ do |variant, options|
        @current_variant = Variant.find variant
        opts = options.split(",")
        #... find the options
        @options = [@option_a, @option_b, @option_c, @option_d]
      end
    
      #... more steps
    

    Keep in mind that at first, that these steps are going to be powered by FastModel and not Rails. Let’s see how we use cucumber_tools to do this.

    Magic Sauce: Rapid Class Design

    After compeleting a RED TDD cycle, Mike’s strategy really takes center stage. Instead of creating classes, he uses FastModel to define the models, including existing models, fields and relationships needed for his feature. He goes even farther and stubs out the interface which is needed to satisify the cucumber test. Let’s look at a specific example.

    class Product < FastModel
        fields :name
        has_many :variants
      end
    
      class Variant < FastModel
        fields :name
        has_many :variant_options
        belongs_to :product
      end
    
      class VariantOption < FastModel
        fields :name
        belongs_to :variant
      end
    
      class VariantCompareChart; end
    

    FastModel

    Without writing migrations, or even loading Rails, you can use FastModel to think about some of the basics of your ActiveRecord models. This let’s you focus on the design of your classes, by iterarting and testing quickly. These FastModels can actually go in the step file making it easier to stay focused on what matters: class design.

    Why stub in a cucumber feature?

    It might seem strange to start stubbing out your class interface inside a cucumber feature. Mike understands this and just like the FastModels above, he isn’t expecting these stubs to stay. He’s using it as a tool to drive very quick class interface modeling. It’s a great way to stay inside one test file and keep the cost of iterating through the class design low. This is a central part of the magic of his strategy. By keeping the design of class' interface directly linked to the design of the feature’s steps, we ensure we only design and test our public interface. Let’s see it in action:

    Then /^the comparison chart header should have options "([^"]*)"$/ do |options|
        opts = options.split(",")
        header = stub(:header) { opts }
        chart = stub(:chart, :header => header)
        VariantComparisonChart.stub(:build_for) { chart }
    
        chart = VariantComparisonChart.build_for(@current_product)
        opts.each do |option|
          chart.header.should include option
        end
      end
    

    Outside: Green. Time to move inside!

    With passing cucumber tests backed by FastModels and stubs, our class design is complete. It’s time to actually start building out the real, but still isolated classes, outside Rails. Using your FastModels and stubs as guides you can TDD up a well unit tested class that does the job. Then swap out the stubs and the FastModels with your real classes and confirm the cucumber cycle is still green.

    How do I use it?

    FastModel and the stubbing library are included as part of cucumber_tool’s no_rails.rb which can be called when executing your cucumber spec:

    cucumber -r no_rails.rb path/to/feature.feature
    

    Mike recommends avoiding the standard env.rb usage of cucumber to avoid loading Rails until you need it later. Mike says these tools were thrown together very quickly to capture the concept of “Feature Isolation” so any pull requests would be greatly appreciated. For more details, please visit Mike’s follow up article.

    rails testing


    Comments