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

Paul: OK. I hadn't thought of that but I can see how it might become a problem. I'll give it some thought. What about the assert_nothing_raised and assert_raised assertions in the test_destroy method? I know we just used assert_nothing_raised to trap a fatal exception in our Category Unit test. But why would the Rails scaffolding use it here? The find doesn't throw a fatal exception.

destroy and test_destroy methods
Figure 24

CB: Good question, Paul. And you're right, in Rails, an unsuccessful find throws an exception, but it's not a fatal one. You probably remember that in our Unit tests we invoked Category.find several times without checking for an Exception. We used assert_not_nil to test that a record was successfully retrieved, and assert_nil to test that a record was not successfully retrieved. Here, Rails' test scaffolding is doing the same tests a different way: by checking to see if a RecordNotFound Exception was raised. Even though a find doesn't really drive the need, I think there are a couple of good reasons to show this approach in the test_destroy method.

First, we know that Rails throws a fatal exception when we try to destroy an object that has children. So, for starters, if Rails is going to scaffold this approach in any of the test methods, this is a pretty good one. Second, we added the check_for_children method to our Category model to fix the problem in the app. If that weren't the case, and if the Category record had any child records in the Recipes table, this test would crash our test case. If we thought there was a risk that the check_for_children method might get lost, instead of POSTing a request to destroy the record, we could invoke Category.destroy inside one of these two assertions. Then the Exception will be trapped and the test method will just pass or fail, depending on the situation, instead of crashing our test case. I'm not suggesting we do that here, because we already have a Unit test, test_cannot_delete_record_with_child, that covers us. In fact, we used assert_nothing_raised in that test. I'm just reminding you that these assertions give us the ability to do it here too.

Paul: I'm ready to give it a shot. Here goes...

I think I want to go ahead and add a test method for the verify. I like the idea of having a specific test that could help decrease our debug time if something happened to it. So I'll add...

def test_verify_gets_are_safe
   get :destroy
   assert_redirected_to :action => 'list'
   get :create
   assert_redirected_to :action => 'list'
   get :update
   assert_redirected_to :action => 'list'  

For the test_list method, I need to add a new assertion to make sure we test for the creation of both instance variables the controller method is creating.

assert_not_nil assigns(:category_pages)

The test_show method is OK, and we've already covered the test methods for the new and create methods in the controller. The test_edit method is OK too. Which brings us to the test_update method.

update and test_update methods
Figure 25

The scaffolded method is only testing a successful update, just like the test_create method was doing before we fixed it. So I think we ought to make the same sort of change here.

CB: That's fine. But before you do, I want to make sure we're on the same page as to why we're doing what we're doing. In the test_create_success_and_failure method you followed the path that the Rails scaffolding had laid down for us; checking the number of records to verify a successful create and failed create. There's nothing wrong with that. But do we really need to do that test in our Functional tests?

Paul: I'm not following you. Why wouldn't we want to make sure the app actually did what it says it did?

CB: Our Unit tests are already testing to validate that the CRUD functionality is working.

Paul: Ahhh... I see what you're saying. So, the question is "do you really want to repeat yourself?" I think you're right. We don't need to do that test again here.

CB: I didn't say we don't. I asked. Let me ask it differently. Is there some value we might get if we did decide to repeat the test here in our Functional test? Say, for example, that we ran our Unit tests and they passed. And then we ran our Functional tests and this one failed. Would that tell us something of value?

Paul: Yeah. It would tell us that something bad had happened to our Unit tests.

CB: Exactly. So, if we think the probability of that happening is high, we might want to go ahead and duplicate the test, but more with the intent of testing our tests, and not so much to retest our app.

Paul: I see what you're saying. Thanks for making me think about it. In fact, I was thinking about doing pretty much the same thing we did in our Unit test; changing the data and then retrieving it to make sure it actually got changed in the database. But now that I think about it, maybe there's a better way to approach this. What do you think about this?

def test_update
   post :update, :id => @first_id
   assert_response :redirect
   assert_redirected_to :action => 'show', :id => @first_id
   post :update, :id => @first_id, :name => ''
   assert_template 'edit'

CB: That looks like a pretty good first stab at it. Let's run the test case and see what we get.

Paul: Good idea.

ruby test\functional\category_controller_test.rb

results of new test_update method
Figure 26

So, what do you think? You good to go?

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

Next Pagearrow