Rspec noob here, just trying to improve my test coverage.
One very basic yet important question I have is just: What kinds of tests go where?
Model tests are straight forward. I just need to test the functionality of the models methods and validations. View tests seem simple. That would just be testing that each view renders the desired data.
What confuses me is what exactly goes in my Request Specs. Most of my rails experience is from following Michael Hartle's Rails Tutorial. His Request specs seem to be based around a series of actions that the user could take in the application. But he also includes test which seem like they should be in the View Specs that I am considering moving elsewhere.
If someone could help me understand what kinds of tests go in Request, that would be helpful.
From the RSpec docs:
Request specs provide a thin wrapper around Rails' integration tests, and are
designed to drive behavior through the full stack, including routing
(provided by Rails) and without stubbing (that's up to you).
With request specs, you can:
specify a single request
specify multiple requests across multiple controllers
specify multiple requests across multiple sessions
Check the rails documentation on integration tests for more information.
From Rails' docs on integration tests:
Integration tests are used to test the interaction among any number of controllers. They are generally used to test important work flows within your application.
If your test has to do with how a single view is rendered (which should be completely decoupled from any actual HTTP request), then it's probably better as a view test. If it has to do with multiple views or multiple requests, then an integration test is probably more appropriate.
Related
I'm finding Rails integration tests relevant for testing flows and I have some questions about the industry standard on replacing controller test (deprecated in rails 5) with integration tests.
Usually we have tiny controllers where we get the parameters, call the right collaborator and prepare the response and it is easy to test it by mocking the collaborator directly on the controller object.
I am concerned about the overhead of migrating every controller test to integration test that persist the db. What are the standards for this case?
Whats the standard when testing just one route/action and not a complete flow?
How can we replace this?:
#controller.stubs(:authenticate).returns(true)
Integration tests are intended to mimic a real user. They're meant to test the entire application in their entirety.
Opinion varies on what this means. To me, it means you should avoid stubbing/mocking completely. Not a single thing stubbed or mocked, everything executed in full. This means that every integration test I write goes through the actual authentication process of typing in a username and password. Some of the steps are redundant, yes.
Integrations tests are slower all around than unit/controller tests. Cutting out the authentication steps likely won't save you enough time to make a difference in the long run (no pun intended).
Rails supports several types of tests:
Model tests
Controller tests
Functional tests
Integration tests
And, with capybara, it can also support:
Acceptance/integration/feature (depends on the author) tests
On some sites I see that these acceptance/integration/feature tests should only test particular flows, leaving edge cases for other kinds of tests. For example:
Integration tests are used to test the interaction among any number of controllers. They are generally used to test important work flows within your application.
http://guides.rubyonrails.org/testing.html#integration-testing
While these are great for testing high level functionality, keep in mind that feature specs are slow to run. Instead of testing every possible path through your application with Capybara, leave testing edge cases up to your model, view, and controller specs.
http://robots.thoughtbot.com/how-we-test-rails-applications
But I also see things like:
Your goal should be to write an integration test for every flow through your app: make sure every page gets visited, that every form gets submitted correctly one time and incorrectly one time, and test each flow for different types of users who may have different permissions (to make sure they can visit the pages they're allowed to, and not visit the pages they're not allowed to). You should have a lot of integration tests for your apps!
https://www.learnhowtoprogram.com/lessons/integration-testing-with-capybara
So, that's my question: In rails, should I include user input form error flows on capybara (or integration) tests?
Or do you think it should be enough to write view tests to test for the existance of flash messages, test failure flows via controller tests with the assigns helper, and only test successful flows through acceptance/integration/feature tests?
Edit The accepted answer was due to the comments.
User input form errors are handled by Active Record validations in Rails. So you could cover these with unit tests. But those kinds of tests only verify that you have validations present on your model. I'm not sure how much utility these types of tests offer other than allowing you to recreate your models in another framework or language and still have your tests pass.
I'm fairly new to rails and trying to do things the "right" way by implementing tests from the get go. Yesterday I used the scaffolding generator to create my first model/view/controller configuration. While I've been told that you really shouldn't be using scaffolding, it was useful so that I could learn how Rails code is structured.
The one thing I noticed was that the automatically generated RSpec was mostly placed in the spec/controllers folder. However when I watched this episode of Railscasts, I noticed that he used the
rails generate integration_test [test_name]
command which placed a single test file in the spec/requests folder. However all of his tests that he wrote interacted with the controllers. What I'm trying to determine is the best practice for where to store these tests.
When should one store tests in the spec/requests folder and when should one store tests in the spec/controllers folder? Any feedback would be greatly appreciated!
Actually those are 2 types of tests. In the controller folder you should create tests to test the controller actions, in the request folder you should place tests to interact with views, wich will actually test all your application parts, and that's why it's named integration test.
Here are some articles about those two types of tests.
http://everydayrails.com/2012/04/07/testing-series-rspec-controllers.html
http://everydayrails.com/2012/04/24/testing-series-rspec-requests.html
Controller specs test the invocation of a single controller action. Typically you won't render views (although you can turn this on), it's also pretty common to stub out a lot of model code. The only interaction you have with the code under test is to invoke a single controller action. You could think of these as unit tests for controllers.
Request specs on the other hand test the whole stack (routing, controllers, views, models etc). Rather than just invoking a single controller action you perform actions closer to what a user would do: visit a page, fill in a form, click a button. Often this will span multiple actions/controllers. For example you might write a request spec that takes a user through the process of adding a product to a cart and then going through the various steps involved in checking out.
You typically use capybara (i think you can still use webrat) to interact with the pages you generate. With a suitable capybara driver javascript on the page will also be executed so, for example, you could test that your client side javascript does the right thing with the json produced by your controller (although you might want to consider writing javascript specs if you have a lot of that)
In the past couple of days I've been making slow progress adding tests to an existing rails app I've been working on for a little bit.
I'm just trying to figure out how much and what kind of tests (unit, functional, integration) will be enough to save me time debugging and fixing deploys that break existing functionality.
I'm the only one working on this application. It's basically an inventory management database for a small company (~20 employees). I didn't add testing from the beginning because I didn't really see the point but I've have a couple of deploys screw up existing functionality in the last little bit, so I thought it might be a good thing to add.
Do I need to test my models and controllers individually AND perform integration testing? There seem to be developers who believe that you should just integration test and backtrack to figure out what's wrong if you get an error from there.
So far I'm using RSpec + Factory Girl + Shoulda. That made it pretty easy to set up tests for the models.
I'm starting on the controllers now and am more than a little bit lost. I know how to test an individual controller but I don't know if I should just be testing application flow using integration tests as that would test the controllers at the same time.
I use integration tests to test the successful paths and general failure paths, but for the edge cases or more in-depth scenarios then I write model/view/controller tests.
I usually write the tests before writing the functionality in the application but if you want to add tests afterwards then I recommend using something like SimpleCov to see which areas of your application need more testing and slowly build up the test coverage for your application.
Writing tests after the app is already written is going to be tedious. At a minimum you should be testing for integration cases (which will test most controller functions and views), and also model tests separately so that your model calls work as you think they should.
For integration testing I find Capybara easy to use.
As a rule of thumb, most of your tests need to be unit, some functional, few integration.
Rails adheres to this wholeheartedly and you should do the same.
At the minimum you have to have:
A unit test for each of your models, add a test case for each of your validations in each of them, and finally a last one to make the sure the model is saved. If you have stuff like dependant: :destroy, touch: true on related models (has_many, belongs_to), add test cases for them as well.
A functional test for each of your controllers. Test cases for each action. Some actions can have multiple responses (like in case of 422 errors) so add another case to test the error response. If you have a authorization filter, test them as well.
Then you can have an integration test for a typical new user flow. Like
Sign Up -> Create Post -> View it -> logout. Another one is to make sure that unauthorized users cannot access your resources.
And from now, do not commit ANY code without the above mentioned tests.
The best time to plant a tree was 10 years ago, second best time is now, so start testing!
I'm trying to get the best codecoverage/development time result
Currently I use rspec+shoulda to test my models and rspec+capybara to write my acceptance tests.
I tried writing a controller test for a simple crud but it kinda took too long and I got a confusing test in the end(my bad probably)
What`s the best pratice on controller testing with rspec?
Here is a gist on my test and my controller(one test does not pass yet):
https://gist.github.com/991687
https://gist.github.com/991685
Maybe not.
Sure you can write tests for your controller. It might help write better controllers. But if the logic in your controllers is simple, as it should be, then your controller tests are not where the battle is won.
Personally I prefer well-tested models and a thorough set of integration (acceptance) tests over controller tests any time.
That said, if you have trouble writing tests for controllers, then by all means do test them. At least until you get the hang of it. Then decide whether you want to continue or not. Same goes for every kind of test: try it until you understand it, decide afterwards.
The way I view this is that acceptance tests (i.e. Cucumber / Capybara), test the interactions that a user would normally perform on the application. This usually includes things like can a user create a specific resource with valid data and then do they see errors if they enter invalid data. A controller test is more for things that a user shouldn't be able to normally do or extreme edge cases that would be too (cu)cumbersome to test with Cucumber.
Usually when people write controller tests, they are effectively testing the same thing. The only reason to test a controller's method in a controller test are for edge cases.
Edge cases such as if a user enters an invalid ID to a show page they should be shown a 404 page. This is a very simple kind of thing to test with a controller test, and I would recommend doing that. You want to make sure that when they hit the action that they receive a 404 response, boom, simple.
Making sure that your new action responds successfully and doesn't syntax error? Please. That's what your Cucumber features would tell you. If the action suddenly develops a Case of the Whoops, your feature will break and then you will fix that.
Another way of thinking about it is do you want to test a specific action responds in a certain way (i.e. controller tests), or do you care more about that a user can go to that new action and actually go through the whole motions of creating that resource (i.e. acceptance tests)?
Writing controller tests gives your application permission to lie to you. Some reasons:
controller tests are not executed in the environment they are run in. i.e. they are not at the end of a rack middleware stack, so things like users are not available when using devise (as a single, simple example). As Rails moves more to a rack based setup, more rack middlewares are used, and your environment deviates increasingly from the 'unit' behaviour.
You're not testing the behaviour of your application, you're testing the implementation. By mocking and stubbing your way through, you're re-implementing implementation in spec form. One easy way to tell if you're doing this; if you don't change the expected behaviour of url response, but do change the implementation of the controller (maybe even map to a different controller), do your tests break? If they do, you're testing implementation not behaviour. You're also setting your self up to be lied to. When you stub and mock, there's no assurances that the mocks or stubs you've setup do what you think they do, or even if the methods they're pretending to be exists after refactoring occurs.
Calling controller methods is impossible via your applications 'public' api. The only way to get to a controller is via the stack, and the route. If you can't break it from a request via a url, is it really broken?
I use my tests as an assurance the my application is not going to break when I deploy it. Controller tests add nothing to my confidence that my application is indeed functional, and actually their presence decreases my confidence.
One other example, when testing your 'behaviour' of your application, do you care that a particular file template was rendered, or that a certain exception was raised, or instead is the behaviour of your application to return some stuff to the client with a particular status code?
Testing controllers (or views) increases the burden of tests that you impose on yourself, and means that the cost of refactoring is higher than it needs to be because of the potential to break tests.
Should you test? yes
There are gems that make testing controllers faster
http://blog.carbonfive.com/2010/12/10/speedy-test-iterations-for-rails-3-with-spork-and-guard/
Definitely test the controller. A few painfully learned rules of thumb:
mock out model objects
stub model object methods that your controller action uses
sacrifice lots of chickens.
I like to have a test on every controller method at least just to eliminate stupid syntax errors that may cause the page to blow up.
A lot of people seem to be moving towards the approach of using Cucumber for integration testing in place of writing controller and routing tests.