It seems like I'm constantly making long-winded replies that would be better off in a blog post or in a book.
Pat On Fri, Apr 18, 2008 at 2:44 AM, Chris Parsons <[EMAIL PROTECTED]> wrote: > Very nice reply Pat. This would make a great blog post if you get a chance. > > Thanks > Chris > > > > On 18 Apr 2008, at 10:15, Pat Maddox wrote: > > > Hey Matt, > > The ultimate test would be one that is focused on one thing such that > the test would > - break every time that thing broke > - break only when that thing broke > - give detailed feedback enabling you to focus on the thing's > subpart necessary to identify and fix the problem > > The ultimate test suite would be the set of such tests that covered > every single concern that exists in a project. > > Some of these are concerns are easier to test than others. Some are > important and lend themselves to automation, thus enjoy great tool > support. Others take something far more abstract like a person's > aesthetic appeal. > > When considering a new test, I should ask myself what problem the > test solves, and what problem I really want the test to solve. I try > to write the test in terms of the second. For example, if I were to > write the following test: > > it "should allow deposits and withdrawals" do > @account.deposit 80 > @account.withdraw 25 > @account.balance.should == 55 > end > > The test would be valuable when the problem is that you need to track > how much money people have in their accounts. > > If you faced a problem such as "Make sure the user sees an error when > they withdraw more than their balance," you would not want a test > like: > > it "should have an overdraft error" do > post :withdrawals, :amount => 1_000_000_000_000 # even bill can't do > this! > assigns[:withdrawal].should have_at_least(1).error_on(:amount) > end > > If this test breaks at some point, we would make a change to the test > or production code in order to make it pass. We wouldn't think of the > problem it was intended to solve though, because we are absorbed in > the problem that initiated the break-inducing change. The danger in > this is that the test appears to be robustly covering something > useful, but in effect can let problems leak through. Imagine that we > remove an important partial from a view. This test, whose ultimate > goal was to ensure that users see an error message, failed to catch > the problem where the message wasn't displayed at all. > > So you should write tests expressing the same level abstraction as the > problem you want them to solve. Somewhere you would need a test like: > > it "should have an overdraft error" do > @page.should include("Can't withdraw more than your balance") > end > > Now you would need some way to get @page. Perhaps you make a request > to a controller, it hits a database, renders a response, and assigns > the response body to @page. > > Or maybe you rendered a view using a fake @withdrawal, and you've got > another test that verifies that you assign @withdrawal in the > controller, and another test verifies that Withdrawal objects get an > error when you try to create them for an amount greater than the > target account balance. And you've got a test that you label an > Integration test to signal the fact that it integrates all of these > pieces. > > You should only write integration tests that check across valuable > boundaries. This does not restrict it to stuff like company-specific > code using an ORM framework, though. Because you should only write > tests that are valuable, sets of layered tests forms a small subsystem > requiring integration testing. > > It is sometimes useful to have layers of tests that enable you to > localize problems. Other times the types of problems you solve will > be trivial or obvious and won't require localization. > > As a simple rule, more tests == more overhead. But if you're missing > certain tests then you will not notice certain problems when they > appear. The art of all of this is identifying the set of tests that > maximizes your confidence and ability to produce valuable software. > > With all that theory out of the way, what can we say about the tests > you presented? > > > describe FeedsController, 'get /feeds/' do > before(:each) do > @request.env["HTTP_ACCEPT"] = "application/xml" > Model.should_receive(:find).with(any_args).and_return > mock_model(Model) > end > > it "should return success" do > get '/' > response.should be_success > end > > it "should return 405 (Method Not Allowed) if HTTP_ACCEPT is text/ > html" do > @request.env["HTTP_ACCEPT"] = "text/html" > get '/' > response.response_code.should == 405 > end > end > > This test would be good in a situation where we had published an API > stating only XML requests were allowed. > > > The second one is much more detailed: > > describe FeedsController, 'get /feeds/' do > before(:each) do > @model = mock_model(Model) > @request.env["HTTP_ACCEPT"] = "application/xml" > Model.should_receive(:find).with(any_args).and_return @model > end > > it "should assign to the model" do > get '/' > assigns[:model].should == model > end > > it "should render feed template" do > get '/' > response.should render_template('feeds/model_feed.xml.erb') > end > end > > This test would be valuable in a context where the XML feed output is > complex. In that case, testing the output directly might not > sufficiently enable us to localize issues. > > If you could write tests that examine the response body, without > reducing the clarity of the example group, you should do so. Fewer > tests == less overhead. > > > Obviously, both are very basic in their implementation, but still, I > ask... If you were writing the specs, which way would you write them? > Thanks for any guidance. > > I hope that, despite the typical "it-all-depends-on-context", I was > able to give you some insight into identifying and analyzing possible > contexts. > > Pat > _______________________________________________ > rspec-users mailing list > rspec-users@rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users > > > _______________________________________________ > rspec-users mailing list > rspec-users@rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users > _______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users