Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker

A 1 minute test suite, 6 months in

Matthew Parker
Friday, June 14, 2013

“Rails is slow, but Rails tests are slower.” Rails may be slow, but I’m here tell you that it’s likely you have only yourself to blame for your slow test suite.

I’ve seen some bad test suites in my day. I was once pulled onto a rescue project that had a total build time of over 24 hours. The health and future of your production code depends on a fast, reliable test suite. On my current project, we believe in fast feedback and we’ve focused a lot of detail and attention to our test suite. It’s paid off in dividends.

We’re six months into a project, and we have a one-minute test suite. That includes both rspec and cucumber (and the time to boot up rails for both). Here’s how we’ve accomplished it:

1. Pyramid

Your test suite is a pyramid. Skinny at the top (journey acceptance tests), fat at the bottom (unit tests). Conceptually, this sounds right, and it’s easy to think that it should just naturally fall out of the BDD outside-in process.

But in reality, it’s not a given. Tools like cucumber make it so easy to spin up new high-level acceptance tests for every edge case you can think of. Taken to an extreme, this can lead to unit-testing at the browser level. That doesn’t mean you were wrong to drive out exceptional paths at the acceptance level. But you should ask yourself this question before you check in: “Will I have any less confidance in my test suite if I don’t check in this acceptance test?” If you have already covered a happy path of a feature, and at least one exceptional path of a feature at the acceptance level, do you then need to cover every other exceptional case at the acceptance level? If you’ve added tests down at lower levels in your stack, then you might already have all the verification value you need.

2. Grooming

We groom our test suite. Not every day. But we watch it and keep it in order. We listen to pain in our test suite (“Why did that change cause so many other tests to break? And why did I have to go to every single test and fix each one of them individually?”). When we revisit old tests, we reconsider it in light of what we know about our application. We keep an eye out for duplicate tests.

3. Refactoring

Our fast test suite practically begs us to refactor. And it turns out, refactoring often leads to simpler designs, objects with fewer methods (and therefore fewer tests), as well as objects that are easier and more natural to test in isolation. Great tests will lead you to refactor your production code, which will lead to even better tests.

4. ActiveRecord Containment

How many tests require you to create data in the database? Ideally, that’s limited to just your acceptance tests and your activerecord model tests. But the reality is never that simple. Although it’s technically possible to accomplish this with any Rails application, it’s not always feasible, or even desired. Libraries you use may make this difficult in some circumstances (e.g., devise).

You might also find yourself in a situation where testing an object in isolation would lead to a very brittle test that knows the entire implementation via stubs and mocks. Worse, you may not see a way to untangle these dependencies. That’s OK. Test the object’s behavior, even if it means integrating with other objects. As your understanding of your application’s problem domain crystallizes, and as more patterns begin to emerge, you will eventually find a way to simplify it, to untangle the dependencies.

5. GC.disable

At one point on our project, our build time crept up to nearly two minutes. We saw ourselves slipping down a very slippery slope. We started running it less, refactoring less, and even finding less motivation to keep our test suite clean. So we threw a chore at the top of our backlog to bring the test suite time back down to a minute. Our PM was naturally wary, asking us to timebox the chore to an hour or two, but we made the case that this was of critical importance to the future of the project, and that the pair working on it would check with the rest of the team after a day.

Near the end of the day, the pair stopped and told the team that they thought they had done all they could, but it only got the test suite down to 1:20. Shaving forty seconds off a test suite in a day is a fantastic feat, and when we looked at what they’d done, we were really impressed. I jokingly suggested disabling GC. And then they actually did it. The test suite time dropped to 48 seconds. I’m not recommending you do this. This is a bit of a nuclear option. If this is the first thing you try to improve your test suite time, then you are missing the point of bringing your test suite time down. But if you feel like you’ve done everything you can to legitimately bring down the time of your test suite, then consider disabling garbage collection. Ruby GC is a beast, with the potential of turning a linear algorithm that creates N objects into a quadratic algorithm. Weigh the pros and cons, and decide for yourself if you can live with the dirty dirty feeling this will give you.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Dumb Controllers, Layered Models

Matthew Parker
Friday, June 7, 2013

“I like to make my views so dumb, there’s no reason to test them.”

Uncle Bob Martin said that at some point, or something very close to it. I’d like to take that a step further: I like to make my (Rails) controllers so dumb, there’s no reason to test them (unless there’s no higher level acceptance test that would exercise the actions in them).

Complicated controllers are painful. Each controller action is like a mini main() function. That’s a lot main()s. The more each action knows about your underlying application, the more brittle your application gets. Think of an action as a launching point into your business domain.

I have some rules (that I occasionally break). It goes something like this:

1) Controllers should be RESTful. No custom actions. new, create, update, destroy, edit, show. That’s it.
2) Controllers should manage a single resource. If you’re instantiating more than a single object in your controller, you’re probably going to regret it.

There’s some great gems out there that can support this restrictive approach to Rails development:

1) Responders (https://github.com/plataformatec/responders). Responders are built into Rails, but the FlashResponder in the responders gem is essential. Replace all of your tedious flash message management code with defaults that can be overriden in your localization files. You can create your own responders to replace any tedious bookkeeping in your controllers.

2) Informal::Model (https://github.com/joshsusser/informal). Since you’re limited to instantiating a single object in your controllers, it’s likely that you won’t get by with just ActiveRecord models. You’ll need to create higher level models that can coordinate the work of all your lower level database models (and action mailers, etc.). Heads up, Rails 4 will obsolete this gem with ActiveModel::Model.

3) ActiveModel::Serializer (https://github.com/rails-api/active_model_serializers). This only applies if you’re creating a JSON API. But if you are, consider this gem. It’s convention over configuration for your API. It works with responders and makes all of the JSON format choices for you so that you can focus on more important things.

I’ll leave with you this:

= simple_form_for @widget_search do |f|
  = f.input :query
  = f.submit

= render @widget_search.results
class WidgetSearchController
  def new
    @widget_search = WidgetSearch.new
  end

  def create
    @widget_search = WidgetSearch.create(query: request.query_parameters)
  end
end
class WidgetSearch
  include Informal::Model

  attr_accessor :query

  validates_presence_of :query

  def self.create(options={})
    new(options).tap &:save
  end

  def save
    return false unless valid?
    @results = Widget.periscope query
  end  

  def results
    @results || []
  end
end

You might have noticed a “periscope” method in there. Yet another fantastic gem.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Building Crash-Proof Applications the YAGNI Way

Matthew Parker
Saturday, June 1, 2013

I’m a YAGNI’ist. I’m vigilant against over-engineering. I will seek out and destroy over-engineered, anticipatory, predictive designs. This wasn’t always the case; early on in my career, I was quite the opposite. I realize now that I suffered from a lack of confidence, and that BDD and extreme programming gives us the power to deal with any problem that arises, WHEN it arises.

The Pivots I’m currently working with on a project are very much of the same mindset. So how did we manage to build a crash-proof application that can persevere in the face of extreme catastrophe, when that was never a goal?

Let me start at the beginning. We were tasked with developing an application that an IT administrator could install into his or her datacenter. After some initial configuration, the application would spider the datacenter, gathering all kinds of data about it. This application would then phone home the data to another application on the Internet, where the customer could review it. It was basically your standard ETL application, with some very non-standard data-sources.

At the very beginning, everything about this application was synchronous. The user would fill out a form, click a button, and wait. While waiting, the application would hit various bits of infrastructure in their datacenter, massage the data, and then phone it back home. Instead of making this customer wait for the form to submit, we could have backgrounded this process right off the bat. But we weren’t collecting quite enough data at first to warrant it. And creating a more graceful user experience wasn’t as high on the priority list as other features.

The number of datapoints we collected started to grow, and at some point, we decided the customer had to wait too long. A minute or two was OK, but 5 minutes? 10 minutes? Unacceptable. We were risking losing customers. So we bit the bullet and backgrounded it. But we took no steps at that point to deal with transitory network failures. Remember, this process is collecting data from other pieces of infrastructure on their network, and phoning that data back home over the Internet. Before (when everything was synchronous) if something went wrong, the user could always resubmit the form. In the new user experience, this was no longer possible. They submitted the form, and were instantly presented with a message informing that data collection is proceeding and to come back later.

We could have written code right at that moment that would anticipate failures. But here’s the rub: we’d experienced no failures up this point in any of our testing. What should we expect to fail? Spidering their infrastructure? All of it? Or were certain aspects of their infrastructure more likely to become unresponsive than others? Or should we expect the phone-home application to stop responding? Anything could fail at any point, but writing code to be resilient the in the face of any type of failure is expensive.

More importantly, we had no story telling us to anticipate failures. And we knew that the cost of writing code that could prepare for any type of failure was prohibitive. We made the case to our product owner to wait.

As the application grew, so did the code, and so did the amount of data points we were collecting. The collection phase took longer and longer, and eventually we started seeing occurrences of failed collections. Every failure wasn’t alike. They happened for different reasons; some we could even control or at the very least curtail (e.g., failures due to rate limiting).

And now we had real stories, driven out by real-world experiences, that we could prioritize against new feature work. Dealing with failures in this way allowed our code to grow over time, to respond to likely failures, while ignoring unlikely ones. Had we attempted to engineer a crash-proof application at the beginning, the results would have been disastrous. But this way, not only did our code evolve in a much more organic and sustainable manner, our understanding of the different types of failures grew over time, giving everyone on the team a better understanding of the technologies our application interacted with.

Today, our application is incredibly resilient. Short of a nuclear bomb, you can not stop this application from completing its ETL. The code is well-factored, readable, and maintainable. And we gradually built in that robustness while still delivering new features. WINNING

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Finding Pivotal

Matthew Parker
Monday, May 20, 2013

The year is 2005. I’m one year out of school, and a year into a job doing PHP web development at a small development firm in Dallas. A co-worker tells me jokingly about extreme programming. He laughs about the absurdity of pair programming and writing tests. Another developer goes rogue and develops an application in something called “Ruby on Rails.” He’s learning the framework at the same time he’s developing the application. I boast that I could have done it in half the time in PHP. That developer takes a job for a company in Seattle doing Ruby on Rails. I spend another frustating year at the company in Dallas.

Fast forward four years to 2009. I’m now living in Manhattan. Burned out on PHP, I’ve spent the last year doing free-lance Ruby on Rails development. I love it. It’s everything I wanted out of a language and framework. It solves all of the common, tedious problems that I faced developing in PHP, and lets me focus on developing my applications.

I land a job at a giant multi-national corporation. The team I’m working with is agile. They hold standups. They have a certified scrum master. They do two week iterations (on projects with fixed scope and hard deadlines). They estimate stories (in hours) (that they write themselves). They write tests (sometimes) (after they write their production code).

It was the best thing that had ever happened to me. And it was hard. And it was painful. We attempted to be agile within an organization of six-sigma blackbelts that would spend 18 months defining a process for defining processes. We didn’t know what we were doing half the time. We got a lot of stuff wrong. But we cared. And we learned. And we got better.

Fast forward three years. I’m still working for that giant company. I get an email from Pivotal Labs. A co-worker warns me, “You know they pair all the time.” I’m a little frightened. I go in for a day-long pairing interview. It’s amazing. I learn more in that one day than I had learned in the last year of work. I pair with developers smarter than me, better than me, and more experienced than me. I take the job.

Every day at Pivotal is like that first day, but even better. I learn something new every single day. I work with other engineers absolutely committed to developing great code. We give new meaning to the word “consultant”, giving our clients not just advice, but pairing with them to act on that advice (and course-correcting when things go wrong). We pair program. We test drive. We collaborate on story specification. We start each week looking at our priorities and estimating our stories. We end each week reflecting in a retrospective, talking about what’s working, what’s not, and what we should do about it. We take breaks throughout the day. We play ping-pong. We’re relentless about self-improvement, team-improvement, project-improvement, and company-improvement. And I’ve never been happier. I’ve never had the privilege of working with such a talented, passionate group of people.

We want to change the way the world develops software. You can too. Do the right thing. Do what works. Be kind.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Method Modeling: A Refactoring

Matthew Parker
Saturday, May 18, 2013

While working on AwesomeResource, I needed to implement functionality that would make the following test pass:


  it "creates readers and writers for any attributes passed in during initialization" do
    article_class = Class.new do
      include AwesomeResource
    end

    article = article_class.new("title" => "Fun")
    article.title.should == "Fun"

    article.title = 'Fun Times!'
    article.title.should == 'Fun Times!'

    expect { article.unknown_attribute }.to raise_exception
  end  

Simple enough. If you initialize an instance of a class that includes AwesomeResource, then you get attribute readers and writers for any hash keys passed in during initialization.

My first attempt at implementation looked something like this:


module AwesomeResource
  attr_reader :awesome_attributes

  def initialize(attributes={})
    @awesome_attributes = AwesomeResource::Attributes.new attributes
  end

  def method_missing(method_name, *args)
    if method_name["="]
      if awesome_attributes.has_key?(method_name[0...-1])
        awesome_attributes[method_name[0…-1]] = args.first
      else
        super
      end
    else
      if awesome_attributes.has_key?(method_name)
        awesome_attributes[method_name]
      else
        super
      end
    end
  end
end

Yuk. All those nested if/else’s didn’t sit well with me. Let me try to clean that up:


  def method_missing(method_name, *args)
    if method_name["="] && awesome_attributes.has_key?(method_name[0...-1])
      awesome_attributes[method_name[0...-1]] = args.first
    elsif awesome_attributes.has_key?(method_name)
      awesome_attributes[method_name]
    else
      super
    end
  end

The best I can say for this code is that it’s more compact. But is it any more readable? Hardly. In fact it’s worse.

Let’s take another look at the first implementation. The nested if/else blocks look awfully similar. Perhaps there’s a polymorphic model lurking there? But what are we modeling? If we were to wrap a class around those blocks, we’d have to be modeling a method. What would that look like? Let’s extract some classes:


  class AttributeWriter
    attr_reader :attributes

    def initialize(attributes)
      @attributes = attributes
    end

    def call(attribute_name, attribute_value)
      raise "Unknown Attribute" unless attributes.has_key?(attribute_name)
      attributes[attribute_name] = attribute_value
    end
  end

  class AttributeReader
    attr_reader :attributes

    def initialize(attributes)
      @attributes = attributes
    end

    def call(attribute_name)
      raise "Unknown Attribute" unless attributes.has_key?(attribute_name)
      attributes[attribute_name]
    end
  end

  def method_missing(method_name, *args)
    if method_name["="]
      AttributeWriter.new(awesome_attributes).call(method_name[0...-1], *args)
    else
      AttributeReader.new(awesome_attributes).call(method_name)
    end
  end

OK, I at least feel like I’m trying to write OO code at this point. There’s a bit of duplication between the AttributeReader and AttributeWriter classes. We could clean that up with template methods:


  class AttributeAccessor
    attr_reader :attributes, :attribute_name

    def initialize(attributes, attribute_name)
      @attribute_name = attribute_name
      @attributes = attributes
    end

    def call(*args)
      raise "Unknown Attribute" unless attributes.has_key?(attribute_name)
      execute(*args)
    end

    def execute(*)
    end
  end

  class AttributeWriter < AttributeAccessor
    def attribute_name
      super[0...-1]
    end
    
    def execute(attribute_value)
      attributes[attribute_name] = attribute_value
    end
  end

  class AttributeReader < AttributeAccessor
    def execute
      attributes[attribute_name]
    end
  end

  def method_missing(method_name, *args)
    if method_name["="]
      AttributeWriter.new(awesome_attributes, method_name).call(*args)
    else
      AttributeReader.new(awesome_attributes, method_name).call
    end
  end

Notice that we've moved the responsibility of stripping off the "=" off the method_name from method_missing down into AttributeWriter.

Hmmm... The base class is pretty abstract. How easy it this code to understand now?

Also, the body of the method missing now looks a lot like a factory method. While in Rome...


  class AttributeAccessor
    attr_reader :attributes, :attribute_name

    def self.from_method_name(attributes, method_name)
      if method_name["="]
        AttributeWriter.new(attributes, method_name)
      else
        AttributeReader.new(attributes, method_name)
      end
    end

    def initialize(attributes, attribute_name)
      @attribute_name = attribute_name
      @attributes = attributes
    end

    def call(*args)
      raise "Unknown Attribute" unless attributes.has_key?(attribute_name)
      execute(*args)
    end

    def execute(*)
    end
  end

  class AttributeWriter < AttributeAccessor
    def attribute_name
      super[0...-1]
    end

    def execute(attribute_value)
      attributes[attribute_name] = attribute_value
    end
  end

  class AttributeReader < AttributeAccessor
    def execute
      attributes[attribute_name]
    end
  end

  def method_missing(method_name, *args)
    AttributeAccessor.from_method_name(awesome_attributes, method_name).call(*args)
  end

I'm now starting to question this code. Is there a good trade off here between indirection and maintainability? I don't think the number of accessors are likely to grow (accessors have consisted solely of getters and setters since the dawn of OO). Between all of the refactorings, I think the first (with its minor duplication) was the easiest to understand.

Yet something still doesn't feel right. Let's look back at the very first code snippet. Notice the `AwesomeResource::Attributes.new` in the `initialization` method? Those are our bag of attributes (they're basically a hash with indifferent access). We keep passing it around to all of our Attribute* classes… perhaps some of this code should have lived there in the first place?


module AwesomeResource
  attr_reader :awesome_attributes

  def initialize(attributes={})
    @awesome_attributes = AwesomeResource::Attributes.new attributes
  end

  def method_missing(method_name, *args)
    awesome_attributes.accessor_for_method_name(method_name).call(*args)
  end
end

module AwesomeResource
  class Attributes < SimpleDelegator
	#...

    def accessor_for_method_name(method_name)
      if method_name["="]
        ->(attribute_value) { self[method_name[0...-1]] = attribute_value }
      else
        -> { self[method_name] }
      end
    end

    def [](key)
      validate_key_exists(key)
      attributes[standardized_key(key)]
    end

    def []=(key, value)
      validate_key_exists(key)
      attributes[standardized_key(key)] = value
    end

    private
    def validate_key_exists(key)
      raise "Unknown attribute" unless has_key? key
    end
    
    #...
  end
end

This feels right. We're now modeling methods with lambdas (Ruby's built in method objects). We've eliminated a tangle of nested if/else blocks without introducing too much indirection. We've maintained high-cohesion within the AwesomeResource::Attributes class despite the new methods.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

AwesomeResource

Matthew Parker
Friday, May 10, 2013

I’ve had it. I’ve had the misfortune to need ActiveResource (an http client library for giving you an ActiveRecord-like API for interacting with restful services) off and on for several years now. Even when it’s worked, it’s never worked well.

Let’s start with the way you configure it. You know how you can use a database.yml file to specify your ActiveRecord connections for different environments? Wouldn’t you expect ActiveResource to work similarly? I mean, it seems unlikely that you’d actually connect to the exact same server endpoint in test, development, and production, right? Too bad. ActiveResource gives you a single way to set a model’s “site”: an attr_writer on the model’s singleton:


class MyModel < ActiveResource::Base
  self.site = ‘http://some-server.com’
end

That global state isn’t a good sign. Even after you hack in your own environment-specific connection code, do you think your model will be thread-safe? Hell no they won’t. If you try to use your models in a threaded environment (e.g., in threaded background worker systems like Sidekiq), you’ll eventually run into a race condition on the model’s singleton “connection” attribute. And your code will raise an exception. Fun.

Let’s talk about JSON. Everyone loves JSON, right? ActiveResource is an old library; when it was originally written, XML was in vogue. The ActiveResource XML support is very mature. It’s JSON support? Broken. That’s right, it’s broken. Has been for years. Sending nested attributes over JSON does the wrong thing. There’s a fix that was merged in a year ago that will be released with Rails 4. In the meantime you can use my “activeresource_json_patch” gem.

Let’s look at the ActiveResource code. It’s a great example of Stunt Programming. Once, when attempting to determine how I might monkey-patch ActiveResource to allow me to set a lambda as the “site”, I stumbled into this method:


      def prefix_source
        prefix
        prefix_source
      end

Um, what? How could that possibly work? It’s INFINITELY RECURSIVE ISN’T IT? Let’s look in the prefix method:


      def prefix(options={})
        default = site.path
        default << '/' unless default[-1..-1] == '/'
        # generate the actual method based on the current site path
        self.prefix = default
        prefix(options)
      end

Wait, the prefix method calls itself too! It’s also INFINITELY RECURSIVE TOO!!! What’s going on? Let’s look one level deeper: the `prefix=` method:


def prefix=(value = '/')
  # snip...
  silence_warnings do
    # Redefine the new methods.
    instance_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1            
      def prefix_source() "#{value}" end            
      def prefix(options={}) "#{prefix_call}" end          
    RUBY_EVAL         
  end       

rescue Exception => e
  logger.error "Couldn't set prefix: #{e}\n  #{code}" if logger
  raise
end

And there you have it. The prefix= method redefines the prefix_source and prefix methods. Thereby avoiding the infinite recursion. FACEPALM

All right, enough complaining. Taken at face value, ActiveResource isn’t actually all that bad. If your needs are incredibly simple, it will likely do the job. And I’ve actually tried to improve the ActiveResource ecosystem over the years. I released a gem that dealt with the nested-attributes-over-JSON bug during the interim until the bug fix is released. I created another gem that made environment-specific site configurations possible. But in the end, I’ve just had it. The code’s a mess. The library is half-forgotten. It’s time for a reboot.

While I was at home recovering from the latest round of the never-ending biological warfare called being a parent, I started a new project on Github: AwesomeResource (http://github.com/moonmaster9000/awesome_resource). Some goals:

  • ActiveRecord-like API
  • Thread-safety
  • Environment-specific configuration
  • Dynamic site, proxy, and password configuration (instead of forcing them to be statically configured in the code)
  • First-class JSON support
  • An integration test suite that would verify that something as critical as JSON support will actually work against a live Rails app
  • A spec that exposes the JSON format the AwesomeResource expects from a server

I’ve got a single feature for SomeModel.create passing. It’s a start.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Getting context in a retro

Matthew Parker
Sunday, May 5, 2013

An agile retrospective is a safe space. We can reflect on the week, think through our victories and defeats, and say what we feel. If your team is a ship, the retrospective is the captain. The captain looks at where you’ve been, thinks about where you’re going, and course-corrects as necessary. Without a retrospective, you’ll likely end up blindly sailing straight into a hurricane.

The retrospective is a critical departure from our day to day process of pair programming. When we pair, we’re focused on the problem directly in front of us. We have to put two minds together to solve that problem, and this requires a surprisingly intense amount of focus. It’s not a process that lends itself to introspection and reflection.

The departure from that day-to-day into a retrospective leads to an interesting communication challenge. Consider this: when we pair, we solve problems by thinking out loud and arriving at solutions collaboratively, organically. Minds meld. Buy-in is a given.

But in a retro, we might arrive at a conclusion without consulting anyone else. And when we present it, the team may disagree, but it’s not always obvious how to resolve that dispute.

Let me give you a real example. Once, on a project, a new client developer joined the team. He was a very experienced, highly competent developer, but he was new to pairing, TDD, XP. A couple weeks in, he stated in a retro, “I think we’re testing just for the sake of it.”

As you can imagine, this ruffled some feathers. We instinctively disagreed, but without knowing the context of his statement, the argument basically boiled down to this:

“No, we’re not.”

“Yes, we are.”

“No, we’re not.”

“Yes, we are.”

Clearly, we needed to know more. And then something happened. Another client developer on the project who had been on it for several months said, “Can you give us some concrete examples of tests that lead you to this conclusion?”

And then everything became clear. Once he described what he was reacting to in the test suite, we were able to hone in on the real issue. The new client developer was seeing tests at the acceptance layer that provided very minimal, common path verification. He wanted to see more edge cases fleshed out, but didn’t yet understand the relationship between high-level integration verification and low-level unit testing.

Whenever you hear a conclusion in a retro that you don’t agree with, ask “Why?” Try to understand what lead them to their conclusion. You can argue with someone until the cows come home, but without understanding the context of their statements, you’re not likely to reach any sort of resolution.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

RAILS HELPERS: FINE AND DANDY LIKE COTTON CANDY

Matthew Parker
Thursday, April 25, 2013

I hated Rails helpers. I saw them as dumping grounds; one-off procedural aberrations in a sea of objects. But I didn’t just complain. I acted. I created the “frill” gem, an implementation of the decorator pattern that I extracted from a project that actually needed a decorator pattern in it’s view layer.

I had another project that seemed ideal for “frill”. It was a data analytics application that required a good deal of manipulation of the raw numeric data points for presentation.

For example, a typical stack of manipulations might look like:

  1. human size a number (for example, turn a “disk_space” attribute representing 1024 bytes into “1 KB”)
  2. if the underlying datapoint exists, render the human sized number, semantically and visually distinguishing the number from the units
  3. if the value is at this point not nil, render the number and its units red or green depending on whether or not it’s healthy
  4. if the underlying datapoint is nil, render a “N/A” message

We implemented the logic using “frill”. Our views looked like a paragon of simplicity:

= environment.disk_space

We had cleverly hidden all of the complexity in a series of modules that frill would dynamically decorate onto our models:

module HumanSizer
  include Frill

  def disk_space
    HumanSizedNumber.new(super)
  end
end

module Renderer
  include Frill
  after HumanSizer

  def disk_space
    if super
      render “human_sized_number”, number: super
    else
      render “not_available”
    end
  end
end

#etc...

The frill library took care of stacking all of the decorators together, extending the objects at runtime with the relevant modules.

So what went wrong?

  1. Indirection. If these were the only two modules in the frills directory, then perhaps it wouldn’t have mattered. But when there was no obvious sign pointing from the view or controller to the relevant modules, you were often left scratching your head (or jumping into a debugger session) to determine the thread of execution when something went wrong
  2. Rigidity. The framework worked well for 90% of cases. But what about the other 10% of the time when you need to alter the presentation stack in some small but suble way? It turns out it was hard to remove existing decorations, or alter their presentation stack.

The latter problem proved especially tenacious. And lacing the decorators with all kinds of conditionals about the random one off cases where such and such decorator didn’t help anything.

HELPERS – STATELESS, COMPOSABLE, FLEXIBLE

When the frill library didn’t work out, we tried using helpers – and discovered that Rails helpers were the answer we’d been seeking all along. We created simple helper methods that we could stack together in pretty much any way our presentation demanded.

= report_NA_for_missing(colorized(human_sized(environment.disk_space)))
= colorized(percentage(environment.density_ratio))

Following the thread of decoration was now trivial.

It dawned on me that there’s really just a simple rule to follow with helpers: keep them stateless. If they’re simple, stateless methods that always return the same output for a given input, then they’re easy test and easy to stack in new and interesting ways. You can even create higher order functions quite easily by taking advantage of the “method” method for turning a method into an object:

= call_reporter_if_nil(method(:report_NA), colorized(human_sized(environment.disk_space)))
= call_reporter_if_nil(method(:report_unknown), colorized(percentage(environment.density_ratio)))

LET THE FUNCTIONAL PROGRAMMING REVOLUTION BEGIN

P.S. I don’t actually endorse that last code snippet.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

BDD starts with a conversation

Matthew Parker
Friday, March 1, 2013

Let’s talk about cargo culting.

When I started doing BDD, I cargo-culted it. I basically had no idea what I was doing. I worked at a company that had never done it. I worked with developers who had never tried it. I read blogs posts and watched screencasts, and I thought I got it. I didn’t. I cargo culted it, and I got it wrong. Result? Pain.

Lots of developers have had a similar experience, and they reached a crossroads: fight or flight. I fought the pain. Did you?

Let’s talk about history.

I want a museum of code. I want to see the first program that had a test written in it. Maybe it’s in C. Algol. Lisp. Maybe it’s a punchcard. Who knows?

Testing started as a movement with Smalltalk. They created SUnit. Smalltalk Unit. It worked. They unit tested their code. That was the easiest type of test to write. It didn’t require any fancy tooling or extra frameworks. Load up an object, assert on it.

Over the years, higher levels of testing emerged. Functional tests, integration tests, acceptance tests. Tooling grew up along side these higher level tests to make them possible.

What was Agile doing?

Agile had conversations. They were talking to their product owners, asking them questions, drawing out concrete examples. Today we call this “Specification By Example.”

Dan North came along. He said TDD was backwards. Why do we starting writing tests at the unit level? We have this process of specification by example with our product owners, and our development process doesn’t line up with that. What if we turned our development process on its head and TDD’ed outside-in? What if that converastion was truly a launching-point into our development process, what if that conversation was a spec that told us when we were done?

BDD is about drawing a line from a conversation with a product owner through the development of a feature and back to the product owner. BDD tools make this possible.

If your BDD process doesn’t start with a conversation with your product owner, you will feel pain.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Cucumber Step Definitions are teleportation devices, not methods

Matthew Parker
Friday, February 1, 2013

Step definition hell. We’ve all heard of it. We’ve all experienced it. The question is, why?

This hell is borne from a simple, yet fundamental, misunderstanding.

When I first learned Cucumber, I instinctively thought of a step definition as a method. I could squint and imagine I was looking at a method. The regex looked roughly like a method “name”. The block arguments were basically like method arguments. The body of the block looked like a method body.

This led me to treat my step definitions like a basic unit of organization within my test suite. A typical step definition body might look like this:

Given /^I have created a widget named "([^"]+)$"$/ do |widget_name|
  visit widgets_path
  fill_in "Name", with: widget_name
  select "sortable", from: "Type"
  check "Fizzable"
  check "Buzzable"

  within(".widget_form .actions") do
    click_on "Submit"
  end

  within(".widget_form .confirmation") do
    choose "Widget Administrator", from: "Approver"
    click_on "Confirm"    
  end
end

Then, I began calling one step definition from another step definition:

Given /^I configured a widget named "([^"]+)$"/ do |widget_name|
  step "Given I have created a widget named \"#{widget_name}\""
  step "Given I have changed the fizbuzz property of my widget \"#{widget_name}\" to \"wuzbang\""
  step "Given the widget administrator has approved the widget \"#{widget_name}\" configuration"
end

And then I nearly killed myself. Because it turns out that step definitions are not methods. Let’s start with the step definition method “name”. It’s not a method name. When you “call” it, you mix in the arguments with the “name”, making the “name” change depend on the arguments. This makes it nearly impossible to do something as simple as an automatic refactor/rename of these “methods” (unless you like writing really complicated regular expressions for find and replace), much less any more complicated refactorings.

Also, there’s a reason real method names are concise. It’s so that we can remember them. With a test suite of more than a couple feature files, you simply can not remember the names of cucumber steps with any satisfying degree of accuracy (which is the same reason you shouldn’t attempt to force your product owner to remember all the exact wordings of existing steps, and instead, let them write the same “step” many different ways).

If you pursue this path of treating step definitions like methods, you will create such a tangled mess that you’ll be left with little choice but to either abandon cucumber entirely or burn your cukes to the ground and start over.

Step Definitions are Teleportation Devices

The only way I’ve found to do sustainable acceptance testing (whether or not it’s cucumber, rspec feature specs, or minitest integration tests) is to create an underlying system of helper methods that represent actions in your application. For example, if you were building Twitter, I would expect to open up your acceptance testing suite and find a module or set of modules with methods that represent the Twitter application domain. That might look something like this:

module Helpers
  def authenticate(user)
    visit root_path
    fill_in "Username", with: user.username
    fill_in "Password", with: user.password
    submit
  end

  def tweet(message)
    visit tweets_path
    fill_in "Tweet", with: message
    #...
  end

  def reply(tweet, message)
    #...
  end

  def dm(message, recipient)
    #...
  end

  #... etc
end

A system of underlying helper methods like this make it really easy to spin up new features. It makes it really easy to let your product owner write the same step 5 different ways. Instead of tracking down the exact wording of the step already in the system and rewriting the feature file your product owner wrote, just take the feature “as is”, spin up new step definition and use your DSL to fill it out (creating any new DSL methods to match new concepts as you go).

If you want to make these module methods available to your cucumber step definitions, you can use the World method:

World Helpers

If you’re using RSpec request specs, use the RSpec configuration:

RSpec.configure do |c|
  c.include Helpers, type: :request
end

Then you’re left with step definitions that transport you from the feature file to your underlying DSL:

Feature: Tweet

  Scenario: Valid tweet
    Given Bob has authenticated
    When Bob submits a valid tweet between 1 and 140 characters
    Then his followers should receive his tweet
    And Bob should see his tweet in his timeline
Given /^I have authenticated$/ do
  authenticate bob
end

When /^Bob submits a valid tweet between 1 and 140 characters$/ do
  tweet valid_message
end

#...

That’s why I think of them as teleportation devices. They transport me from the written world (the feature file) to a world of code (my application’s acceptance DSL).

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Matthew Parker

Matthew Parker
San Francisco

Subscribe to Matthew's Feed

Author Topics

bloggerdome (5)
open source (1)
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >