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
Adam Milligan

There is no Agile

Adam Milligan
Saturday, January 3, 2009

On occasion, someone will ask me what I do or, more commonly, ask me what Pivotal does. The title on my business card says “Agile Developer,” which nearly inevitably leads to one of a few reactions, almost all of which boil down to this:

“What exactly does Agile mean?”

You know what? It’s a good question. And, after thinking about Agile for several years now, here’s my answer: Agile originally was, and still is, a marketing term.

Allow me to explain.

We’ve been writing software, in one form or another that most people would recognize as such, for some time now. Scott Bain sets the mark at about 30-35 years[1]. Considering the acceleration of technological advancement in the past century, this is a fairly long time.

In the early days, most programs were small, relatively simple, and written by a handful of developers or fewer[2]. So, the general approach to writing them was to put a number of programming language instructions one after another until the computer did the right thing. Eventually.

I believe it was Watts Humphrey who described this type of programming as “ad hoc development.”[3]

Of course, computers got bigger and badder, and software got more complex, and ad hoc quickly stopped scaling. So, people went looking for a new model to stop the hemorrhaging, and they found waterfall. Now, there’s nothing wrong with waterfall for processes that conform to defined process modeling, such as manufacturing Model-Ts, installing sewer lines, or preparing breakfast cereal. But, most people realize these days that it’s not so hot for processes with requirements that may change.

Years passed, and smart people got stuff done by solving different problems in different ways. And, some patterns started to emerge. And, better yet, some truly clever people started to recognize these patterns and write about them and codify them. However, they realized, astutely, that human beings are a hard-headed bunch who don’t like to change their ways. They needed something to shake programmers out of their collective torpor; they needed something flashy to sell; they needed a manifesto. What better thing to offer to an industry plagued by setbacks and missed deadlines than Agile?

To this day the marketing pitch continues; Agile vs. Waterfall, new vs. old, crazy vs. stodgy.

What can we infer from this brief history?

  • The move to waterfall wasn’t motivated by an understanding of the specific challenges of software[4]; it was an attempt to apply a working process, any working process, to an industry that was suffering from far too many failures due to a lack thereof.
  • Many of the principles that have come to be reflected in “agile” processes or “agile” methodologies simply emerged as consistently successful patterns from years of software projects.
  • Over time, a few people have recognized these patterns, studied them, tried to explain them, and expanded upon them to make them even more effective[5].

The conclusion I draw is this: so-called “Agile” is actually nothing more than a collection of good ideas, based on years of collective experience, for improving how we do our jobs as software writers. Or, to put not too fine a point on it, professionalism.

So, if doing “Agile” things means doing your job well, the term ceases to have meaning. As it should. No one should have to sell us good ideas, we should embrace them and have the discipline to stand by them.


[1] Emergent Design: Addison Wesley, 2008

[2] This wasn’t true of all software, of course; the punch card systems that ran the Apollo space program weren’t hacked out by a couple guys in a garage. But, systems like that have their own, mostly time-related, problems.

[3] I loaned out my copy of Winning With Software and can’t immediately find a reference, should anyone care to confirm or deny this credit.

[4] This relates to my rejection of the term “software engineer.” No one really agrees on what writing software is, but we have to call it something. If we call ourselves engineers then we sound like we know what we’re doing. After all, look at all the good stuff in the world that engineers have built.

[5] Pair programming is the classic example of this: code reviews improve code, more frequent code reviews improve code more. How about we code review all code, all the time, as it’s written?

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Better View Testing with Elementor

Pivotal Labs
Tuesday, November 25, 2008

We’ve got a few mantras at Pivotal. One of them has to do with testing all the time. It’s a Good Thing, for sure. Until recently though, I had always inserted a tacit “except for views” to the end of it. The reason for my reservations wasn’t the fact that view tests can be brittle. Any test can be brittle. I didn’t like testing views because it seemed like the test I was writing never really described the code I was writing. Let’s look at a typical view test to see what I mean:

describe "/posts/index.html.erb" do
  def render_view
    render "/posts/index.html.erb"
    response.body
  end

  before(:each) do
    assigns[:posts] = [
      stub_model(Post, :name => "First!", :body => "first body."),
      stub_model(Post, :name => "Second!", :body => "second body.")
    ]
  end

  describe "assertions using have_tag" do
    it "renders posts" do
      render_view
      response.should have_tag(".post", 2)
    end

    it "renders post headers" do
      render_view
      response.should have_tag(".post .post-name", "First!", 1)
      response.should have_tag(".post .post-name", "Second!", 1)
    end

    it "renders post bodies" do
      render_view
      response.should have_tag(".post .post-body", "first body.", 1)
      response.should have_tag(".post .post-body", "second body.", 1)
    end
  end
end

This snippet uses the have_tag helper. It’s somewhat slow, and to my eyes, expresses intent about as well as an apple floating in a top-hat filled with perfume. The “tag” is a selector? The content filter is just the second argument? And the last argument is the amount of “tag” the response should have? You can test like this, but why would you?

I’ve also seen a more manual pattern, using a library like Hpricot or Nokogiri to parse the response body, then asserting on the results of that:

describe "assertions using Nokogiri" do
  def doc
    @doc ||= Nokogiri(render_view)
  end

  it "renders posts" do
    doc.search('.post').should have(2).nodes
  end

  it "renders post headers" do
    headers = doc.search('.post .post-name')
    headers.should have(2).elements
    headers.detect { |element| element.text == "First!" }.should_not be_nil
    headers.detect { |element| element.text == "Second!" }.should_not be_nil
  end

  it "renders post bodies" do
    bodies = doc.search('.post .post-name')
    bodies.should have(2).elements
    bodies.detect { |element| element.text == "first body." }.should_not be_nil
    bodies.detect { |element| element.text == "second body." }.should_not be_nil
  end
end

It’s faster, since it’s not using have_tag, but still not very expressive. CSS selectors are still littered across the it statements, but at least it’s only once per test. Still, using detect to find content is no good. And I don’t think CSS selectors have any business in it statements at all. That seems like asserting on the name of a method being called, not its behavior.

The solution!

Given my problems with the above approaches, I created a gem that allows the following assertion syntax:

it "renders posts" do
  result.should have(2).posts
end

it "renders post headers" do
  result.should have(2).post_headers
  result.should have(1).post_header.with_text("First!")
  result.should have(1).post_header.with_text("Second!")
end

it "renders post bodies" do
  result.should have(2).post_bodies
  result.should have(1).post_body.with_text("first body.")
  result.should have(1).post_body.with_text("second body.")
end

What’s a result? And how does it know how many posts, post_headers, and post_bodies it has? The result is defined in a before block like so:

require 'elementor'
require 'elementor/spec'

include Elementor

attr_reader :result

before(:each) do
  @result = elements(:from => :render_view) do |tag|
    tag.posts         ".post"
    tag.post_headers  ".post .post-name"
    tag.post_bodies   ".post .post-body"
  end
end

The elements method allows you to name your CSS selectors using the tag block argument. The tag object uses method_missing to register your names. The :from option specifies a method to be called that will return some raw markup.

Naming selectors alone was a huge win for me, but there are a few other cool bits about the @result object. First, you get to use the with_text helper for filtering content. You’ll also get a with_attrs helper for filtering based on a hash of attribute values.

The project is called Elementor, and you can install it like so:

[sudo] gem install elementor

The code is on the GitHub here: github.com/nakajima/elementor (and you can see the CI build here). Take a look at the specs for all of the examples of what you can do. Hopefully, you’ll find it as useful as I have. If not, please share your reasons in the comments!

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Joe Moore

Standup 11/18/2008: Unbelievable has_many :through Gotcha

Joe Moore
Tuesday, November 18, 2008
  • One team discovered a jaw-dropping issue with has_many :through. Given the following:

    class User < ActiveRecord::Base
      has_many :user_photos
      has_many :photos, :through => :user_photos
    
    • a_user.photos.create will create and persist both a Photo object and the UserPhoto join object
    • photo = a_user.photos.build followed by photo.save will create and persist the Photo object only, and will not persist an appropriate UserPhoto join object.
  • Rails 2.2: Test::Unit::TestCase extentions have been removed from Rails Core and are now in ActiveSupport::TestCase. As stated in the Groups Thread about this, use ActiveSupport::TestCase instead of Test::Unit::TestCase in test/test_helper.rb.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Chad Woolley

Notes on Google Chrome Compatiblity

Chad Woolley
Tuesday, November 18, 2008

Pivot Jonathan and I were recently working on support for Google Chrome in Pivotal Tracker. Tracker’s extensive JsUnit test suite made this a lot easier.

Here’s some quick notes I took on the issues we ran into.

Don’t try to directly mock the ‘reset’ method on a Form Element

This was the original mocking code in one of our JsUnit tests:

var resetCalled = false;
widget._uploadForm.reset = function() { resetCalled = true; };

This permanently blew away the “reset” method, so it was undefined when called in a subsequent. To fix it, we did this in our form builder method:

var element = Element.create("form");
element.nativeReset = element.reset;
element.reset = function() { element.nativeReset() };

Hash keys sort differently

We had a testHash.keys() being compared to a hardcoded array. Chrome sorted the keys differently (apparently non-deterministically, so we had to do an explicit sort:

assertArrayEquals(['10001', '10002', '10003', 'endOfList'].sort(), $H(itemListWidget.draggables).keys().sort());

It wasn’t good to depend on the keys order in the first place, but it worked under IE, Firefox, and Safari.

The same hash sorting bug bit us in a much more obscure way. There was some threading test code that simulated timeouts/concurrency using a mock clock. Previously, the test code was dependent on the order in which the functions were added to a hash the mock “clock”. This broke with a different hash sorting order. We had to simulate some additional “ticks” to make the test pass.

Mozilla, but not Gecko

The browser string returned for Chrome by one of our utility functions, BrowserDetect.browser(), is “Mozilla”. However, for some of our simulated keypress events in tests, the “Gecko” version did not work.

Specifically, we had to use “KeyboardEvent” instead of “KeyEvents”, and “initKeyboardEvent” instead of “initKeyEvent”. See the table in this mozilla doc page.

Here’s the code we used to handle both cases:

evt = document.createEvent('KeyboardEvent');
if (typeof(evt.initKeyboardEvent) != 'undefined') {
  evt.initKeyboardEvent(eventName, true, true, window, false, false, false, false, options.keyCode, options.keyCode);
} else {
  evt.initKeyEvent(eventName, true, true, window, false, false, false, false, options.keyCode, options.keyCode);
}

The UserAgent (request.user_agent) returns

Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.3.154.9 Safari/525.19

The ‘sort’ function does not preserve order of equivalent elements

The following page outputs ‘ACBD’ under Chrome:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<body>
<a href="#" onclick="alert(doSort()); return false;">Sort</a>
<script type="text/javascript">
  function doSort() {
    var myArray = [
      {id: "A", sortVal: 0},
      {id: "B", sortVal: 1},
      {id: "C", sortVal: 1},
      {id: "D", sortVal: 2}
    ];
    var sorted = myArray.sort(function(a,b) {return a.sortVal - b.sortVal});
    return sorted[0].id + sorted[1].id + sorted[2].id + sorted[3].id;
  }
</script>
</body>
</html>
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Joe Moore

Standup 11/17/2008: Google Chrome Gotchas

Joe Moore
Monday, November 17, 2008

Interesting Things

  • We recently updated Pivotal Tracker‘s extensive JSUnit test suite to be compatible with the Google Chrome browser. Check out the extensive Notes on Google Chrome Compatiblity post by Pivot Chad.

  • ActiveScaffold + Rails 2.2 = BOOM. ActiveScaffold will work with Rails 2.1 if you get the version from github. Read about it.

  • Rails 2.2 + Rspec 1.11 = FAIL
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Adam Milligan

Why is the sky blue? When does the wind blow? Why do tests fail?

Adam Milligan
Monday, November 17, 2008

Here’s an interesting mental exercise that recently I’ve found more and more valuable:

  1. Test drive your code (duh).
  2. Before each time you run your tests, no matter how small the changes you’ve made, ask yourself why the tests will fail. Don’t just gloss over this; be explicit. Say it out loud or write it down, if that helps. If you have a pair, tell your pair.

You might be surprised how often your assumptions turn out wrong. And, you might be surprised what you learn when you explicitly state those assumptions and then have to justify them when they do turn out wrong.

If you don’t test drive, or you don’t always test drive, (and you’re still reading), ask yourself this: what happens those times when my assumptions are wrong and I don’t have tests to protect me?

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Pattern for Functional Testing

Pivotal Labs
Friday, November 14, 2008

Regular Selenium tests (in Java) might look like:

selenium.open("/login");
selenium.type("id=username", "bob");
selenium.type("id=password", "password");
selenium.click("Login");
selenium.waitForPageToLoad();
selenium.click("My Account");
selenium.waitForPageToLoad();
assertEquals("bob", selenium.getText("//table[2]/tr[3]/td[2]/");

After a few tests, this kind of thing becomes painful to manage. The typical solution is to create a bunch of constants for IDs and Xpaths, but that doesn’t help too much.

Fellow Pivot Mike Grafton came up with a cool pattern for improving on this. The idea is to create a class representing each page of your web app. Each class contains two types of methods: a bunch of action methods (clickMyAccountLink(), typeUsername()), and a bunch of inspection commands (isLoginButtonEnabled(), getLoggedInUsername()).

When an action takes you to a new page, the corresponding action method returns a new class representing that page. When it stays on the same page, the method just returns “this”. This allows methods to be chained to make the tests more readable.

Here’s how that test would look using this new pattern:

MyAccountPage myAccountPage = new LoginPage(selenium)
  .typeUsername("bob")
  .typePassword("password")
  .clickLoginButton()
  .clickMyAccountLink();

assertEquals("bob", myAccountPage.getLoggedInUsername());

The constructor of each page class should validate that it’s on the correct page (waiting if necessary, and perhaps asserting on the page title).

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Joe Moore

Standup 11/07/2008: Selenium for Flash

Joe Moore
Friday, November 7, 2008

Interesting Things

  • Teaser: Selenium for Flash! We’ve developed a Selenium-like framework for Flash. It’s pre-alpha, and needs to be extracted from it’s current home inside a project. Are you interested in a Selenium-like framework for Flash, or have you written one yourself? Let us know!
  • STI-weirdness. Rails surprise of the day: given a query of a has_many :photos where Photos has STI subclasses (got that?) Rails will build a SQL query that includes the subclass types of Photo, which you might not want:

    foo.photos.find_by_type("Photo")
    # query will have "... WHERE type IN ('Photo', 'OriginalPhoto', 'ThumbnailPhoto')"
    
  • It appears that the retardase_inhibitor might not work with Rails 2.1.X due to fixes in ActionMailer.

  • JetBrains has been hard at work: they have released both a new Ruby plugin for IntelliJ, and a ruby-specific IDE (based on IntelliJ) named RubyMine.
  • Check out Pivot Jonathan’s wife’s art exhibit at Artist-Xchange Gallery in San Francisco, Friday 11/7 from 7-10pm:

    Artist-Xchange Gallery
    3169 16th Street
    San Francisco
    CA 94103

Ask for Help

“I want to create a custom launcher for Firefox 2 and Firefox 3 with different profiles. Perhaps the real question is how do we create a custom version of a Mac application launcher, passing in the arguments we need?”

… without having to invoke it on the command line every time.

“We’re trying to delete cookies in our Controller, but they keep appearing in the headers anyway.”

Suggestion: make sure you are specifying your URL paths and domains correctly.

“Why won’t our CSS and other assets load the first time when accessing an SSL-protected domain on Engine Yard?”

It’s most likely not Engine Yard or Firefox 3′s fault. More research needed.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Joe Moore

Standup 10/15/2008: this_method; dynamically creating tables for testing

Joe Moore
Wednesday, October 15, 2008
  • Where am I? — Ever need to find the name of the method you are currently within? Here’s a this_method method! The magic is in the REGEX, of course.
module Kernel
  private
  def this_method
    caller[0] =~ /`([^']*)'/ and $1
  end
end
  • One project wanted to test a very ActiveRecord-specific Module in an isolated, generic way. After spending time researching techniques of mocking and stubbing the many, many ActiveRecord methods that would be touched, they decided to just dynamically create an ActiveRecord and a DB Table for it on the fly! They even used single table inheritance (STI)
  describe "MyMagicModule Mixin" do
    before(:all) do
      ActiveRecord::Base.connection.create_table "some_base_models",
                                                   :force => true do |t|
        t.string   "name"
        t.string   "type"
        t.integer  "some_model_b_id", :limit => 11
      end
    end

    after(:all) do
      ActiveRecord::Base.connection.drop_table "some_base_models"
    end


    class SomeBaseModel < ActiveRecord::Base;end

    class SomeModelA < SomeBaseModel
      include MyMagicModule

      belongs_to: :some_model_b
    end

    class SomeModelB < SomeBaseModel
      include MyMagicModule
    end

    it 'should use special belongs_to stuff from MyMagicModule' do
       model_a = SomeModelA.create!(
                        :name=> "Model A",
                        :some_model_be => SomeModelB.create!(:name => "Model B"))
       # test the functionality from MyMagicModule
    end
  end
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

New York Standup 10/2/2008

Pivotal Labs
Thursday, October 2, 2008
  • Selenium on several machines was failing with Connection Refused errors. This turned out to be caused by IPv6 entries (for example, “::1 localhost”) which were added by a recent MacOS upgrade. Commenting out those entries seemed to fix the problem (or work around it, anyway).

  • Some people expressed a style preference, in rspec, for “pending” rather than an empty “it” block, to make it easy to search for pending tests. Excessive pending tests may be an anti-pattern. On the other hand, writing pending tests, at least temporarily, may be a good way to sketch out an area of functionality before it is implemented.

  • We have sometimes found that editing selenium tests (but not other ruby files) in IDEA is incredibly slow (as in, 30 second pauses). Two things to try are removing the gems directory from the project (in favor of just those gems which you need to be able to look at in IDEA), or at least removing the selenium gems. Another such example is that IDEA can be really slow editing the end of a long fixture file. This is probably IDEA 7.0.3 or 7.0.4.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (780)
  • rails (113)
  • testing (88)
  • ruby (83)
  • ruby on rails (70)
  • jobs (62)
  • javascript (55)
  • techtalk (44)
  • rspec (38)
  • ironblogger (32)
  • productivity (30)
  • activerecord (29)
  • gogaruco (29)
  • git (28)
  • nyc (27)
  • rubymine (26)
  • bloggerdome (23)
  • mobile (22)
  • process (21)
  • pivotal tracker (20)
  • cucumber (20)
  • jasmine (19)
  • design (18)
  • ios (18)
  • webos (17)
  • objective-c (17)
  • android (16)
  • palm (16)
  • "soft" ware (16)
  • fun (15)
  • tracker ecosystem (15)
  • ci (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • bdd (14)
  • gem (13)
  • css (13)
  • tdd (13)
  • selenium (12)
  • goruco (12)
  • bundler (12)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
  • mojo (10)
  • chef (10)
  • api (10)
Subscribe to testing Feed
  1. ←
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. 8
  10. 9
  11. →
  • 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 >