DEV Community

Danny Tseng
Danny Tseng

Posted on • Updated on

Testing with RSpec - Request Spec

When building applications with Test Driven Development(TDD) approach, it is a common practice for developers to write testing code to test different parts of the application individually such as unit test or as a whole like integration test. In the context of a Rails application with the MVC design pattern, the unit test is for testing the model(which is M in the MVC) and the integration test is for testing the combination of model, view, and controller(which is MVC in this case).

One commonly used testing tool is RSpec and it is a domain-specific language that is used to test Ruby code for Rails applications. In the case of using RSpec for writing testing specs for Rails application, the equivalent of unit test concept is the model spec and the equivalent of integration test concept is the feature spec in RSpec. There is also the controller test that involves testing the routes and end-points of the application. For the controller test which is sometimes referred to as functional test, the RSPec equivalent is the Request Spec.

Controller Spec was used to be the spec that was utilized in testing controller actions. However, since the release of RSpec 3.5, it is recommended by the developers of RSpec to use Request Spec instead because of the advantages that Request Spec has such as speed for which Request Spec is much faster than the Controller Spec.

When working with request specs, the common way is to specify one or more request and response cycles for different controller actions in order to test if the endpoints are being hit correctly as well as the response body contains correct information when returned.

To create a request spec file, you can simply type in the below command into the terminal where "activities" in the line below is the name of the controller you are testing.

    rails g rspec:request users

It is going to generate a spec folder with a requests folder inside it. The users spec file will be inside the requests folder. Inside the request spec file for users, you will see some code inside the file such as below.

    RSpec.describe "Users", type: :request do


    end

Within the do-end block is where you will be able to add test code for context and examples to verify the different controller actions. The "describe" keyword creates an example group that contains a collection of related tests.

    RSpec.describe "Users", type: :request do

      describe "GET users#index" do
        it "should get index" do
          get '/api/v1/users'
          expect(response).to have_http_status(200)
        end
      end

    end

As shown in the above code snippet, you can have "describe" within another "describe" to create nested "describe" example groups for specifying what each example group is for. Another keyword that is interchangeable with the "describe" keyword is "context." One thing to keep in mind is that "context" cannot be used as the top-level method in place of "describe" specifically for the first line of code above. The describe method takes a string as an argument and the string usually states what the particular test is for. Within the example group, there is the method, "it," which is the individual test or "example." The method "it" also takes a string as an argument which is used to describe what the particular test is for. In the above case, the context or example group is for "GET" which is the index action of the specified controller. The test or example is for testing if the endpoint can be hit and response can be returned properly with a 200 OK success status code.

To test the controller action for HTTP verb "POST," we can simply create another example group for testing the create action of the controller. Just like the example above, we can provide a route for the post action to hit along with the parameters for creating the specific object of the controller which in this case is the user. See the example below.

    describe "POST users#create" do
      it 'create user with valid attributes' do
        user_params = { user: {
          first_name: 'John',
          last_name: 'Doe',
          username: 'JDoe,
          password: '123',
          email: 'johndoe@example.com'
        }}
        post '/api/v1/users', :params => user_params.to_json, :headers => { "Content-Type": "application/json" }
        json = JSON.parse(response.body)
        expect(response).to have_http_status(201)
      end
    end

As you can see above, the use of request specs is pretty much to specify one or more request and response cycles from end to end. Below is another example of request and response cycle and this time is for the update action.

    describe 'PUT activities#update' do
    it 'should update the description' do
      activity_params = { activity: {
        name: 'Basketball',
        description: 'Basketball 3v3',
        user_id: user.id,
        latitude: 47.7170,
        longitude: -122.3015,
        address: 'b coffee'
      }}

      new_activity_params = { activity: {
        name: 'Basketball',
        description: 'Change from 3v3 to 5v5',
        user_id: user.id,
        latitude: 47.7170,
        longitude: -122.3015,
        address: 'b coffee'
      }}
      activity = Activity.create(activity_params[:activity])
      put "/api/v1/activities/#{activity.id}", :params => new_activity_params.to_json, :headers => headers
      json = JSON.parse(response.body)
      expect(json["activity"]).to include("description" => "Change from 3v3 to 5v5")
    end
  end

This particular test is for verifying if the description of the activity has been updated properly. When you make a network request through the request spec, a response will be returned. In this case, the information about the object will be stored in the response.body which can be turned into a JSON object by applying JSON.parse on it which is the equivalent of response.json() in JavaScript. To verify the value of any particular attribute, the include method is useful to see if the value of a particular attribute is as expected.

Request specs are pretty useful in testing the endpoints and different controller actions to see if the functions are working properly. It is an integral part of test driven development and is a common tool for doing functional testing. Above is just a quick look into writing request specs and how each controller action can be targeted in the different example groups and example for testing.

References:
http://rspec.info/blog/2016/07/rspec-3-5-has-been-released/#rails-support-for-rails-5
https://www.codewithjason.com/difference-integration-tests-controller-tests-rails/
https://relishapp.com/rspec/rspec-rails/v/3-9/docs/generators
https://github.com/rspec/rspec-core/blob/395866dc0df64b5719429f803e2a056275bc7bd3/features/Upgrade.md#context-is-no-longer-a-top-level-method

Top comments (0)