Simulating Requests

Learn about requests test performance, goals, and simulating requests in requests test.

Ideally, our controllers are relatively simple. The complicated functionality is in a model or other object and tested in our unit tests for those objects. One reason this is a best practice is that models are easier to test than requests because they’re easier to extract and are used independently in a test framework.

Note: A request test should test the behavior of a single request. A request test should not fail because of problems in the model.

Request tests performance

A request test that overlaps with model behavior is part of the awkward middle ground of testing that the Rails 5 changes are designed to avoid. If the request test is going to the database, the test is slower than it needs to be. If a model failure can cascade into the controller tests, it’s harder than it needs to be when isolating the problem.

Request tests goals

A request test should have one or more of the following goals:

  • Verify that a normal, basic user request triggers expected model or workflow object calls. Test doubles work extremely well for this purpose.

  • Verify that a side effect, such as an email or background job, is triggered as part of a user request.

  • Verify that an ill-formed or otherwise invalid user request is handled properly, for whatever definition of “properly” fits our application.

  • Verify security, such as requiring logins for pages as needed and testing that users who enter a URL for a resource they shouldn’t be able to see are blocked or diverted. (We’ll discuss this more in the chapter “[Testing for Security.]”) This is also a good candidate to be offloaded into unit testing with, for example, the Pundit gem.

Simulating requests in request tests

Our request tests in Rails 5 will usually start with a simulated request. To make this simulation easier, Rails provides a test method to simulate each HTTP verb:

  • The delete method
  • The get method
  • The head method
  • The patch method
  • The post method
  • The put method

Each of these methods works the same way. (Internally, they all dispatch to a common method that does all the work). A full call to one of these methods has one argument and up to five optional keyword arguments. The argument is a URL string, which can be passed as a string or as a Rails URL helper. The keyword arguments are listed below alphabetically:

  • The as argument: This specifies the content type we want returned, especially if that content type is :json.
  • The env argument: This is a hash of environment variables. (It’s not immediately clear why we’d want to set those in a request spec).
  • The headers argument: This is a hash of simulated header information we might want to be passed.
  • The params argument: This is the params hash for controllers, including anything that might be in a query string or a route variable. We can use the params hash to cover items that might otherwise be arguments to the URL.
  • The xhr argument: If true, this tells the controller to treat the fake request as an Ajax call.

So, a contrived call might look like this:

get(projects_url, params: {id: @project.id}, xhr: true, as: :json)

The fixture_file_upload helper method

If one of the param arguments is an uploaded file, for example, from a multipart form, we can simulate that using the Rails helper fixture_file_upload(filename, mime_type), like this:

Get hands-on with 1400+ tech skills courses.