advertisement

Print

Cookin' with Ruby on Rails - Integration Tests
Pages: 1, 2, 3, 4, 5, 6, 7

now Rails knows what to do with an empty url
Figure 5

Paul: And if we change our mind about the default URL for our site in the future, all we have to do is change that one line in routes.rb? Nice!

CB: That's Rails! ;-)

CB: OK. So let's move on to our Integration tests. The first thing to note is that while Rails creates a test/integration directory for us, it doesn't attempt to scaffold any test code for us. So all we've got at this point is an empty directory. Let's put something in it. First, I'll create the skeleton for our test case. I'll name the file cookbook2_integration_test.rb

empty integration test case
Figure 6

Paul: That looks a lot like the other test cases we started with.

CB: Yep. We'll need our fixtures, so I've included them. And I put our standard "test truth" method in there just so we can test our skeleton before we start adding any actual test methods. The only real difference is in the class definition. In both the Unit and Functional tests we've worked on, the class we used was Test::Unit::Testcase. For Integration tests, we use a new class:

ActionController::IntegrationTest

The IntegrationTest class inherits from Test::Unit, which gives us access to the same methods we have access to in our Unit and Functional tests. Let's run our skeleton test case just to make sure I haven't forgotten or fat-fingered anything. I'll change into the test/integration directory just to save myself some typing. Then...

ruby cookbook2_integration_test.rb

testing our test case skeleton - no problems!
Figure 7

CB: And it looks like we're good to go! So let's write some tests that'll let us add to and otherwise change our app in the future without the fear of unintentionally breaking things. Paul, you want to take over?

Paul: Sure, CB. Boss, you ready?

Boss: You bet. How's this going to work?

CB: I'd recommend you start with just walking through the app and having a conversation about it. We want to make the tests speak your language and the best way I know to learn that language is to hear you use it.

Boss: That sounds easy. OK. I guess it's really pretty simple at this point. We want to make sure that when a visitor goes to our home page, it has the right title, the right footer, and the right stuff in the middle.

CB: Well then, let's write a test that makes sure our home page satisfies those requirements!

def test_the_home_page
  browse_to_the_home_page
  check_the_home_page_title
  check_the_home_page_footer
  check_the_home_page_content
end

Boss: Rails knows how to do all that?

CB: Naw. Rails is good, but that'd be real magic! ;-) Rails, and that includes the Test Framework we're using, is, at one level, really just a layer consisting of a bunch helpers that make it a lot easier to write the Ruby code that runs at the layer below. I find it easiest to think of the test framework as a mirror of the application framework.

Test Framework Application Framework
DSL test code <--> DSL app code
test framework <--> app framework
Ruby test code <--> Ruby app code

Our tests are just code written in a different language to describe the same thing the application code describes. Code is code, and we'll apply the same principles and patterns to our test code that we apply to our application code; especially modularity and encapsulation. So, now I need to write the test methods that our test_the_home_page test invokes.

The first thing we need to do is browse to the home page.

def browse_to_the_home_page
  get "/"
  assert_response :success
  assert_template "recipe/list"
end

The first line creates a GET request for the page at the application root. Our mocked web server treats the request pretty much just like Mongrel would if we entered "http://localhost:3000/" in our browser. And just like Mongrel, our mocked web server invokes the Rails controller/action we specified in routes.rb. The application constructs an HTML page in response and passes it back to the web server to deliver to the browser that requested it.

The second line is an assertion that tests the response object to verify that when it delivers the response to the browser, the web server sends along a "200 OK" status code. And the final line asserts that the view template that Rails used to render the page was the list.rhtml template in the app\views\recipe folder.

Now that the home page has been rendered, we can examine it to ensure that it contains the items we expect. And the next item is one that does a good job of illustrating what I was saying earlier about making our tests speak the same language our business partners use. In programmer speak, Boss, title is usually taken to mean the wording that appears on the very top of the browser window. Paul, hit the home button on the browser for me, please. The title here is "Mozilla Firefox Start Page - Mozilla Firefox."

showing Boss the browser title
Figure 8

CB: But I'm pretty sure that's not what you meant, Boss. Am I right?

Boss: You're right, CB. I was talking about the bold "Online Cookbook" line at the top of the page being rendered inside the browser window.

CB: That's what I thought. One of the nice things about Rails and its test framework is that we can very easily accommodate using language that the business understands in a way that makes it unlikely that the intent will be misunderstood by the developers. In this case, our test code will be ...

def check_the_home_page_title
  assert_select "h1", {:text=>"Online Cookbook"}
end

The assert_select line tells the framework to look for a set of <h1> tags on the rendered page surrounding the text "Online Cookbook." So the business isn't forced to understand the technical difference between a page title and a page heading, and the developers can very easily see what's actually expected. And now, on to our footer...

def check_the_home_page_footer
   assert_select "a", {:text=>"Create new recipe", href=>"recipe/new"}
   assert_select "a", {:text=>"Show all recipes", href=>"recipe/list"}
   assert_select "a", {:text=>"Show all categories", href=>"category/list"}
  end

The assertions here tell the framework to find the three hyperlinks we've put on the page for navigation.

And finally, we create the method that checks that the page contains a table.

def check_the_home_page_content
  assert_select "table"
end

So, at this point, our test case looks like...

initial test case
Figure 9

Pages: 1, 2, 3, 4, 5, 6, 7

Next Pagearrow