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 theparams
hash for controllers, including anything that might be in a query string or a route variable. We can use theparams
hash to cover items that might otherwise be arguments to the URL. - The
xhr
argument: Iftrue
, 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.