34

While learning Rails I've created an application with a Domains controller nested below a Customers controller. I'm using Rails 2.3.4 and it's been a learning experience. I managed to get the below routing set up:

    customer_domains GET    /customers/:customer_id/domains(.:format)          {:controller=>"domains", :action=>"index"}
                     POST   /customers/:customer_id/domains(.:format)          {:controller=>"domains", :action=>"create"}
 new_customer_domain GET    /customers/:customer_id/domains/new(.:format)      {:controller=>"domains", :action=>"new"}
edit_customer_domain GET    /customers/:customer_id/domains/:id/edit(.:format) {:controller=>"domains", :action=>"edit"}
     customer_domain GET    /customers/:customer_id/domains/:id(.:format)      {:controller=>"domains", :action=>"show"}
                     PUT    /customers/:customer_id/domains/:id(.:format)      {:controller=>"domains", :action=>"update"}
                     DELETE /customers/:customer_id/domains/:id(.:format)      {:controller=>"domains", :action=>"destroy"}
           customers GET    /customers(.:format)                               {:controller=>"customers", :action=>"index"}
                     POST   /customers(.:format)                               {:controller=>"customers", :action=>"create"}
        new_customer GET    /customers/new(.:format)                           {:controller=>"customers", :action=>"new"}
       edit_customer GET    /customers/:id/edit(.:format)                      {:controller=>"customers", :action=>"edit"}
            customer GET    /customers/:id(.:format)                           {:controller=>"customers", :action=>"show"}
                     PUT    /customers/:id(.:format)                           {:controller=>"customers", :action=>"update"}
                     DELETE /customers/:id(.:format)                           {:controller=>"customers", :action=>"destroy"}
                root        /                                                  {:controller=>"customers", :action=>"index"}

However, all tests for the Domains controller are failing due to routing errors.

For example the following test (generated by Rails' resource generator) fails, as do all other tests in the DomainsControllerTest class.

class DomainsControllerTest < ActionController::TestCase
  test "should get index" do
    get :index
    assert_response :success
    assert_not_nil assigns(:domains)
  end
end

It fails with the error:

No route matches {:action => "index", :controller => "domains"}

This makes sense since the default routes no longer exist and the Domains controller requires a @customer to be set. I've spent an afternoon looking for the needed change, but almost every site talks about Rspec tests instead of regular Rails tests.

How do I modify the domains_controller_test.rb so it will understand the nested resource?

Ryan Bigg
  • 106,965
  • 23
  • 235
  • 261
Martijn Heemels
  • 3,529
  • 5
  • 37
  • 38

3 Answers3

47

Passing the customer_id with the requests would do. Something like this :-

class DomainsControllerTest < ActionController::TestCase
  test "should get index" do
    get :index ,:customer_id=> 1
    assert_response :success
    assert_not_nil assigns(:domains)
  end
end
Rishav Rastogi
  • 15,484
  • 3
  • 42
  • 47
  • That seems to work, Rishav. Is there any way to DRY this up, instead of passing the id to each separate test? – Martijn Heemels Sep 20 '09 at 15:38
  • 1
    Martijn, that's what you're gonna have to do. Your routing must know the customer to work. I suppose you could write a test helper to replace the "get" call that autofills the customer_id part of the hash, but that seems like a bad idea unless you're writing many, many tests against the DomainsController. – Barry Hess Sep 20 '09 at 18:29
  • 1
    Just want to point out why DRYing this up could be a bad idea b/c it could be hard to read and debug. DRY code is great, but not when it sacrifices readability. In a test, you definitely want to be able to see what's going on. If you can DRY it up without sacrificing readability or adding fragility to the test, do it. – WattsInABox Sep 18 '13 at 04:19
1

According to your routes, domains don't longer exist outside of the context of a customer. The request needs a customer_id for the named routes to match.

The way to do this in your test is:

test "should get index" do
  get :index, :customer_id=>joe
end
ndp
  • 21,546
  • 5
  • 36
  • 52
  • 2
    I'm not sure I understand the problem. Domains create would be `post :create, :customer_id=>joe`. The `customer_id` will still be found in the URL for a nested resource. – ndp May 03 '12 at 18:31
1

There is a gem called nester that solves this exact problem. It generates methods that give you back a domains_path and similar as if your routes weren't nested. When your views and tests reference, for example, domain_path(domain), the routes are expanded to customer_domain_path(domain.customer, domain). There is also a ActionController::TestCase helper that adds :customer_id => @domain.customer for methods like get, post, etc.

The default generated functional tests and views work out of the box with this approach. (Disclaimer: I wrote it).

adamlamar
  • 4,629
  • 2
  • 27
  • 22