Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Controller test for nested resources

can't go over the problem with CREATING INDEX ROUTE TEST for nested attribute. I've been trying all my ideas but nothing works

I have Service model and ServiceRoute model.

ServiceRoute belongs_to Service

and

Service has_many ServiceRoutes.

Here is my controller:

class ServiceRoutesController < ApplicationController

before_action :set_service_route, only: [:show, :update, :destroy]
before_action :set_service, only: :create


def create
  @service_route = @service.service_routes.build(service_route_params)

  if @service_route.save
    render json: @service_route, status: :created, location: @service_route
  else
    render json: @service_route.errors, status: :unprocessable_entity
  end
end


private

  def set_service_route
    @service_route = ServiceRoute.find(params[:id])
  end

  def set_service
   @service = Service.find(params[:service_id])
  end

  def service_route_params
    params.require(:service_route).permit(:routes_list, :service_id)
  end
end

I create ServiceRoute with build method

My routes.rb file:

resources :services do
  resources :service_routes
end

Test "should create service route" has an error:

ActionController::UrlGenerationError: No route matches {:action=>"index", :controller=>"service_routes"} missing required keys: [:service_id]

test "should create service_route" do
 assert_difference('ServiceRoute.count') do
  post service_service_routes_url, params: { service_route: {routes_list: 
  @service_route.routes_list, service_id: @service_route.service_id } }, 
 as: :json
end

How should I refactor this test to match the route: /services/:service_id/service_routes

like image 443
elmuzyk Avatar asked Jun 25 '26 16:06

elmuzyk


1 Answers

Testing nested routes may bring some confusion with regards to where the tests should be placed. Although nesting may give you the feeling the tests for the children should be written inside of the parent's tests, in fact they should be isolated in their own Test Case.

Rspec infers the controller being tested using the Test Case class name. You can also indicate it using a class method .tests.

ActionController::TestCase will automatically infer the controller under test from the test class name. If the controller cannot be inferred from the test class name, you can explicitly set it with +tests+.

Although you might be tempted to use the path as the first argument to the method .post, TestCase expects an action name such as :index, :create, :update and so on. The correct syntax for your test should be:

post :create, params: {
  service_id: @service_route.service_id,
  service_route: {
      routes_list: @service_route.routes_list 
    }
  }

Here's a snipet of the documentation for the TestCase.get.

  # Simulate a GET request with the given parameters.
  #
  # - +action+: The controller action to call.
  # - +params+: The hash with HTTP parameters that you want to pass. This may be +nil+.
  # - +body+: The request body with a string that is appropriately encoded
  #   (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
  # - +session+: A hash of parameters to store in the session. This may be +nil+.
  # - +flash+: A hash of parameters to store in the flash. This may be +nil+.
  #
  # You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with
  # +post+, +patch+, +put+, +delete+, and +head+.
  # Example sending parameters, session and setting a flash message:
  #
  #   get :show,
  #     params: { id: 7 },
  #     session: { user_id: 1 },
  #     flash: { notice: 'This is flash message' }
like image 105
Alexandre Angelim Avatar answered Jun 28 '26 05:06

Alexandre Angelim



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!