Structure of BDD tests - ruby-on-rails

I'm digging into Capybara and rspec, to move from TDD to BDD.
My generators make a whole lot of directories and spec tests,
with directory structure similar to this:
spec
controllers
models
requests
routing
views
I think that most of this is TDD rather than BDD. If I read here:
"A great testing strategy is to extensively cover the data layer with
unit tests then skip all the way up to acceptance tests. This approach
gives great code coverage and builds a test suite that can flex with a
changing codebase."
Then I figure that things should be quite different
something on the lines of:
spec
models
acceptance
Basically I take out controllers, requests, views, and routing to just implement tests as user case scenarios in the acceptance directory with Capybara, Rspec.
This makes sense to me, though I'm not sure if this is the standard/common approach to this.
What is your approach?
Thanks,
Giulio

tl;dr
This is not a standard approach.
If you only test models and feature specs... then you miss out on the bits in the middle.
You can tell: "method X broke on the Widget model" or you can tell "there's something wrong while creating widgets" but you have no knowledge of anything else.
If something broke, was it the controller? the routing? some hand-over between the two?
it's good to have:
extremely thorough testing at the model-level (eg check every validation, every method, every option based on incoming arguments)
rough testing in the middle to make sure sub-systems work the way you expect (eg controllers set up the right variables and call the right templates/redirections given a certain set of circumstances)
overall feature testing as smoke-tests (eg that a user can go through the happy path and everything works the way they expect... that if they input bad stuff, that the app is throwing up the right error messages and redisplaying the forms for them to fix the problem)
Don't forget that models aren't the only classes in your app.. and all classes need some kind of testing. Controllers are classes too. As are form and service objects, mailers, etc.
That said - it's common to consider that view-tests are going overboard. I'm also not keen on request-tests our routing test myself (unless I have something complex which I want to work right, eg lots of optional params in a route that map to interesting search-patterns)

Related

Rails and Testing, why testing controllers is not enough?

I was wondering, as testing a controller in rails run the associated views (even if not shown) and integrate many models concerns (by saving, updating,...), testing controller should be almost enough for all applications near enough of CRUD classical architecture. Am I wrong?
Furthermore, views can be tested in the browser, as eyes can be quicker to check than describing everything in a test (and they can achieve CSS control too.)
Thank you for your point of view!
PH
Testing only your controllers will tell you that, broadly, your app is working, at least in terms of not 500'ing or whatever. But can you be sure that it is doing the exactly correct thing? If all you need to test is standard resourceful behaviour like "given params[:id], is the record with id <params[:id]> loaded?" then just testing the controller might be enough.
But, you will inevitably add more complicated behaviour into your models. In this situation, your controller may set some variables or something, without raising an error, by calling a model method. At this stage, it's much cleaner to test the model method directly, to make sure that given a particular set of conditions, it does the right thing.

How would you go about testing partial, RSpec?

Lets say I have a very complicated view with a very complicated test spec.
I would like add another feature to it and I would like to drive the development with RSpec.
I would like to add contacts to Employee class.
Somewhere in the employee.html.erb I add the line:
#lots of data rendered so far
<%= render #employee.contacts %>
#even more here
At the moment I don't really know what _contact.html.erb partial needs to look like. To test it directly from my main view spec for employees, show.html.erb_spec.rb, I would have to do a lot of set up populating or mocking different database employee is build from(projects, departments etc.).
All I need to test for now is that partial, I don't care about departments, projects and other things this employee currently belongs to and I don't want to spend my time on mocking them all up. I will probably add it to my main spec later on and create everything needed to test the entire view but not just jet.
Do you have any technique or way for creating an instance of Employee, adding a few Contacts to it, and testing the view for just the partial?
Without worrying about all the other things the Employee is built from.
UPDATE FOR IGEL ANSWER:
I agree with you, and testing for expect(...).to receive(:render) is 100% enough for request spec and I would go no further if I already had a partial template.
I just wanted to go extreme and drive my div's and span's through RSpec. Not testing for behaviour of new feature but it look. Do you suggest its not worth it?
I don't test views(structure) at all but I have seen a few documents like Rails Tutorial where Michael is not only testing behaviour but as well structure. I thought I'll give it a go:)
The correct answer is to avoid very complicated views. If there is almost no logic in the view, then there is maybe no need to test it. Extract the logic into presenters, that way it's much easier to test. I don't test my views with view tests, because maintaining them it usually not much fun. Some feature specs give me enough security here.
If you still want to test it, you would probably expect that render was called with the expected arguments:
expect(view).to receive(:render).with(#employee.contacts)
Not sure if the view is available via view.
PS: Don't just avoid to write complicated views, avoid complicated code. This is really hard, but also absolutely worth the time. Almost every developer can hack together something probably working, but creating something easy to understand and to change will help everybody including your future you. Code is read ten times more often than it is written/changed, so we have to optimize for that.
I didn't have time to write a short letter, so I wrote a long one instead.
-- Mark Twain

How should I test my Rails app?

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!

How to test Controller Actions for simple mistakes

When scaffolding controllers it will create tests for that model, do the test have the ability to check for runtime errors for the whole page including rendering the .erb
If so can tests scan for common typos in the .erb for example checkbox instead of check_box
Because silly typos take a stupid amounts of time to figure out because the code looks right.
It would be good if there was a plugin that would use a service to check if it's a common typo or gotcha.
In general I test the controller in the controller tests and the views in the view tests.
In more detail, I will test that given the right input to the controller, it produces the right output. I usually mock out the model(s) involved and concentrate on what work matters inside the controller. In my view tests I simply validate that the things on the page look like what I want them to look. I also use Jasmine to test javascript when I have more complex interactions in that body of code.
I put a lot of stock in this separation of tests.
I use rspec for my model, view, controller, routing and request tests and I write failing tests and then implement the feature/method to make those tests go green. And also I run rake spec before checking in. This gives me good coverage on the type of things you're concerned about.
There is also a gem called autotest that will run a test each time you save them (or perhaps some other granularity). If your test is green it will run the entire suite. I don't use it because I don't like how aggressively it does this but I have friends that swear by it.

Should I really test controllers?

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.

Resources