Cookin' with Ruby on Rails - More Designing for Testability
Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11

CB: Now, Paul. You're making me think you didn't do your homework. ;-) Remember the links I gave you last time? Lemme refresh your memory. The author of "Testing the Rails," a fellow by the name of Evan "Rabble" Henshaw-Plath, called these...

quoting from Testing the Rails
Figure 12

Paul: OK, I do remember that now. But I really didn't understand what he meant by "simulate a web request." I thought the test framework was sending requests to Mongrel and then looking at the replies. Is that not what's going on?

CB: Actually, no. That's not what's going on at all. I realize we had Mongrel running during our last session, so it's reasonable that you interpreted things that way. And there are other frameworks that do work that way. I had an email exchange with a fellow named Philip Plumlee who's written a Short Cut that we'll probably be talking about if Boss has ideas for this app that require us to use Ajax. I thought Phlip (that's the screen name he uses on the forums) explained it pretty well. He said...

expaining Rails' use of the Mock the Webserver pattern
Figure 13

Paul: So, we don't need to be running Mongrel to run our tests?

CB: Nope. Here. Let's prove it. Shut down Mongrel and then close the command windows and the browser. So, now I've got the Instant Rails manager and the MySQL server running, but nothing else. So, let's open a new command window and give this Functional test scaffold code a test drive.

Paul: OK.

ruby test\functional\category_controller_test.rb

first run of the category_controller_test
Figure 14

Paul: Ouch! I can see that Mongrel didn't need to be running, but what's that about? That's not because of the changes we made to the code, is it? I mean, it looks like all the tests failed, and we only changed one method in the category controller.

CB: Nope. The error message says Rails can't find a fixture named "first." If we ran the recipe controller test we'd get the same results. I could have avoided this but I wanted you to see it. We haven't worried about fixture names yet because, where we are right now, we haven't needed to pay them any real attention. Later on, though, fixture names are going to become a lot more important in terms of the readability of our tests. This is only speculation on my part, but I think this is sort of an example of what DHH calls "syntactic vinegar." Rails generated both the Unit and Functional tests at the same time and both use the same fixtures. So, I'm pretty sure Rails knows that the fixtures are named "one" and "two." ;-) This is, I think, a message from DHH and team: "Scaffolding is not an excuse not to think. Do you want to use the same fixtures for your Unit and Functional tests? Or do you need different fixtures? How are you going to name your fixtures so that your code communicates your intent?" Those sorts of questions are important, and you and I are going to spend some time talking about them in the very near future. Right now, though, let's just fix the test case so we can bring you up to speed on the basics. You OK with that?

Paul: Yeah. I think that's a good idea. I'm not sure I could contribute much to a discussion on those topics without understanding at least the basics.

CB: Cool. The fix is easy. Take a look at the setup method on category_controller_test.rb again. The culprit is the line at the bottom of it that says:

@first_id = categories(:first).id

We need to change that to:

@first_id = categories(:one).id

And since we already know that our model validation is going to fail if we try to save a category record without a name, let's go ahead and fix the test_create method to avoid that. Change...

post :create, :category => {}


post :create, :category => {:name => "new category"}

Now, let's rerun our category controller test. Then I'll walk you through what's going on.

ruby test\functional\category_controller_test.rb

a successful run of our category_controller_test
Figure 15

Paul: The tests all passed this time. That's good. But what are those warnings about?

CB: Those are telling us that somewhere, either in our application or in Rails itself, there's code that's using POST on a hyperlink and that Rails is not going to support that in the future. Rails is headed toward full support for the REST pattern, so, if you're not already familiar with it, you might want to get familiar with it. There's a good intro at And, as with all things Rails, Google is your friend ;-)

In this case, there's nothing wrong with our app. The warnings above are being generated by some Rails internal code. Some folks find them a little annoying, but I think it's probably good to have the reminders.

So now let's talk about what's going on in those test methods. Let's start right at the beginning of our app's execution cycle. To get a complete view of what's being tested and how, we need to take a look at the index method in our controller, the test_index method in our controller test case, and the setup method that gets run prior to all our test methods.

the controller, test, and setup for the index method
Figure 16

CB: Remember that before each of the test methods in our test case is executed, our setup method is going to run. Like we talked about a minute ago, the first three lines of that method create a set of objects that are going to be used in pretty much every Functional test we're ever going to write. The first line tells Rails which controller we'll be testing and to create a new instance of it. I can't remember whether we covered this in our first meeting, but, even if we did, it bears repeating: Rails applications live for exactly one request-response cycle. Every request from a browser to a Rails app contains information in the URL about which controller and method are being requested. Rails creates a new instance of the controller being requested for every request. To execute our tests, since we're "mocking the web server," we need to tell Rails to create a new instance of the controller we're going to be sending requests to. That controller instance expects to receive a request object and expects that it will provide content for a response object. So, we need to create those objects too.

When we execute our test_index test method, the first line tells Rails, actually the test framework portion of Rails, to use the request object we just created and send a request to the index method in our Rails application's category controller using the GET request type. The next line tells the test framework to check the response object for a status code in the 200 to 299 range. A successful status code means that the server successfully received, understood, accepted, and processed the request. The last line tells the test framework to check to make sure the page "being sent back to the browser" was rendered using the list.rhtml template. I say "being sent back" in quotes because, remember, we're not actually going to send anything anywhere. The test framework is going to fake Rails into believing it's talking to a web server as normal, but the framework is actually sending all the requests and capturing all the responses.

How's that feel?

Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11

Next Pagearrow