ruby-on-railstestingcontrollerrack-test

When writing Rails controller tests, why use #post vs #get vs #delete?


I'm realizing I don't understand the rationale for why Rails controller tests / specs are structured the way they are.

When writing controller tests, we're encouraged to more or less treat the controller as a unit and write unit tests accordingly, caring only about the controller's inputs and outputs. For example, we set up a certain state of the database and perhaps stub some Devise methods to simulate a user being logged in, and when we call post :create or whatever, we append a hash of params sent to that controller action.

Then, for the output, we look at the resulting state of the database, we check the response HTTP code and redirect etc., and any assigned variables that will be passed on to the template rendering process.

The controller is just a Ruby class whose public methods are called to execute various RESTful actions. So in controller specs we don't load URLs like /kittens/new; instead we call the controller action directly, like get :new. Routes aren't supposed to be relevant; they're just another part of the system that decides which controller action to call for a given request.

So why do we have to specify the HTTP method (like get, post, put, delete) when calling the controller action? Isn't this an external detail, part of how routing works?

Out of curiosity, I took one of my controller specs and switched all these methods around, ending up with things like delete :show and get :create. Nothing broke. So I'm thoroughly confused: why do we distinguish between these methods if they're not relevant detail to the code that we're testing in controller specs?


Solution

  • I'm not exactly sure if this is why it's still around, but pre-REST, a lot of apps used the same endpoint for new and create. (Like, you might have a controller with a new_review action, and if you hit it with a GET, it'd show the review form, and if you hit it with a POST, it'd save the review.) In these controller actions, you'd see if request.post?.

    Nowadays, you see that a lot less often (thankfully), but it's still supported. So by putting the HTTP method in your test, you can make sure that the request object behaves the way you expect it to in your controller action.