Rails 3: Adding tests to production code - ruby-on-rails

What's the best way to add tests to a code who has been in production for some time? I imagine that RSpec is not the best solution given that its goal is for TDD.

It's not ideal to write tests after the code has already been written. In fact it can be kind of hard if you do not write tests and code together since "non-tested" code is seldom well designed for testing.
However, writing tests to production code is better than not having tests at all. I recommend that you take the approach of adding tests to every method you want to change, every new method and every bug that is found in the code (before you start fixing it)... and build the test suite that way.
What library you use is less important. Use the one you like best. My personal preference is Test::Unit but RSpec and Shoulda are very popular too.

Related

Writing test cases(Rspec) for existing application

Being Rspec noob, i dont know this question make sense. BUt i am really confused and couldnt get solution for this.
We have one new requirement, asked us to write Rspec test cases for existing project. They have application with rails3 that already working but they want rspec cases to be written now
My question is
Writing Rspec cases for existing application does make sense?
Can we write spec for already working project
Should we follow any guidelines to write specs for already written app
Thank you
As dpassage rightly pointed, your question has nothing to do with rspec. It's about unit testing a legacy code.
Writing Rspec cases for existing application does make sense?
Can we write spec for already working project
Yes. It's best when you write your tests first before you write the application. It's never too late to write tests, starting late does not hurt.
Testing legacy code: Adding unit tests to legacy code
Should we follow any guidelines to write specs for already written app
My two cents on this:
Add tests incrementally
Test first the most important parts of your application. i.e. Start writing the unit tests first and focus on important models (instead of all models). Once you test your models move to functional tests(controllers) and integration tests. Remember: To eat an elephant we need to eat one piece at a time.
If your existing models are difficult to write test, it implies that you need to refactor your code. Refactor to make your models simple to test.
Always write unit tests for new models/implementation from NOW.
Useful references:
TDD by example - Kent beck book
Effective testing: http://www.confreaks.com/videos/917-railsconf2012-testing-best-practices-or-the-five-habits-of-highly-effective-tests
It absolutely makes sense to add test cases to existing code; it will help keep the application working as future enhancements and bug fixes are made.
There's nothing about rspec that makes this different in particular; it's just a testing framework, enabling you to test your code many different ways.
There's a fair amount of literature on writing tests for legacy code; in combination with good documentation such as The Rspec Book, you should be able to get started.
I wrote this gem https://rubygems.org/gems/autogen_rspec.
So this gem has the feature complete but I feel it requires some polishing.
How it works?
So after we install this gem and run a server, the gem starts to log all the request, response and assert rspec style. So if you install this on a QA env and as the QA team will test for regression, all the URLs will be logged to a file with responses.
It is up to you to select which test needs to be picked a stored in your test file.

What are all the pieces to an effective TDD strategy?

I'm really getting frustrated with learning how to properly develop software using TDD. It seems that everyone does it differently and in a different order. At this point, I'd just like to know what are all the considerations? This much is what I've come up with: I should use rspec, and capybara. With that said, what are all the different types of test I need to write, to have a well built and tested application. I'm looking for a list that comprises the area of my application being tested, the framework needed to test it, and any dependencies.
For example, it seems that people advise to start by unit testing your models, but when I watch tutorials on TDD it seems like they only write integration test. Am I missing something?
Well, the theme "how do you TDD" is as much out there in the open as the theme "how do you properly test?". In Ruby, and more specifically in Rails, rspec should be the tool to start with, but not be done with. RSpec allows you to write Unit Tests for your components, to test them separately. In the Rails context, that means:
test your models
test your controllers
test your views
test your helpers
test your routes
It is a very good tool not exactly rails-bound, it is also used to test other frameworks.
After you're done with RSpec, you should jump to cucumber. Cucumber (http://cukes.info/) is the most used tool (again, for the Rails environment) to write integration tests. You can then integrate capybara on cucumber.
After you're done with cucumber, you'll be done with having tested your application backend and (part of) its HTML output. That's when you should also test your javascript code. How to do that? First, you'll have to Unit test it. Jasmine (http://pivotal.github.com/jasmine/) is one of the tools you might use for the job.
Then you'll have to test its integration in your structure. How to do that? You'll come back to cucumber and integrate selenium (http://seleniumhq.org/) with your cucumber framework, and you'll be able to test your integration "live" in the browser, having access to your javascript magic and testing it on the spot.
So, after you're done with these steps, you'll have covered most of the necessary steps to have a well-integrated test environment. Are we done? Not really. You should also set a coverage tool (one available: https://github.com/colszowka/simplecov) to check if your code is being really well tested and no loose ends are left.
After you're done with these morose steps, you should also do one last thing, in case you are not developing it all alone and the team is big enough to make it still unmanageable by itself: you'll set a test server, which will do nothing other than run all the previous steps regularly and deliver notifications about its results.
So, all of this sets a good TDD environment for the interested developer. I only named the most used frameworks in the ruby/rails community for the different types of testing, but that doesn't mean there aren't other frameworks as or more suitable for your job. It still doesn't teach you how to test properly. For that there's more theory involved, and a lot of subdebates.
In case I forgot something, please write it in a comment below.
Besides that, you should approach how you test properly. Namely, are you going for the declarative or imperative approach?
Start simple and add more tools and techniques as you need them. There are many way to TDD an app because every app is different. One way to do that is to start with an end-to-end test with Rspec and Capybara (or Cucumber and Capybara) and then add more fine-grained tests as you need them.
You know you need more fine-grained tests when it takes more than a few minutes to make a Capybara test pass.
Also, if the domain of your application is non-trivial it might be more fruitful for you to start testing the domain first.
It depends! Try different approaches and see what works for you.
End-to-end development of real-world applications with TDD is an underdocumented activity indeed. It's true that you'll mostly find schoolbook examples, katas and theoretical articles out there. However, a few books take a more comprehensive and practical approach to TDD - GOOS for instance (highly recommended), and, to a lesser extent, Beck's Test Driven Development by Example, although they don't address RoR specifically.
The approach described in GOOS starts with writing end-to-end acceptance tests (integration tests, which may amount to RSpec tests in your case) but within that loop, you code as many TDD unit tests as you need to design your lower-level objects. When writing those you can basically start where you want -from the outer layers, the inner layers or just the parts of your application that are most convenient to you. As long as you mock out any dependency, they'll remain unit tests anyway.
I also have the same question when I started learning rails, there're so many tools or methods to make the test better but after spending to much time on that, I finally realized that you could simply forget the rule that you must do something or not, test something that you think it might have problem first, then somewhere else. Well ,it needs time.
that's just my point of view.

How do I go about adding testing to a finished rails app?

Right so I started a rails app [3.0.9] a while back and didn't include any testing, and now that I'm nearing the end of it, the daunting task looms. I don't actually have testing experience yet. A cardinal sin, I know, but nothing that can be done about it now other than fix it.
Luckily in my case, it's a relatively small app with just 4 models, and only a few controller methods per model. The business logic is mostly non-trivial. Where would I start testing here? Should I do cucumber tests and add RSpec to the exceptions? What combination should give me enough coverage to confidently push it to production when the time comes?
What I generally advise in a case like this (a finished site and no tests) is to write black-box tests first with cucumber. This will make sure you can have very quickly a test-suite that will cover purely the operational side: it will make sure the entire website works (and keeps working) as intended.
Only then I would start writing tests (I prefer rspec, but that is a matter of opinion), based on a need. The cucumber tests go through all layers, so everything could be covered.
With rspec you can test your models, controllers and views in isolation which is really nice, but will be a lot of work to do afterwards (although for only 4 models ...).
Rspec is awesome.
If you plan to do any UI testing then watir or selenium are very good open source tools. You can use rspec or test::unit for using watir or selenium.
Adding tests for a small app with just 4 models is not that difficult. Any test is better than nothing. RSpec or Test::Unit will do for the beginning.
Consider this an opportunity to learn a testing framework that you think you'll like and would use in future projects since the application you have written seems relatively small.
I don't know if Heckle would work or if there is something like it, but that could help you check that your tests actually are testing what you want.

Practicing BDD with integration tests -- do I need unit tests too?

At present, my development process flows like this:
I describe the expected behaviour as an integration test using using WebRat
I write the Ruby on Rails code to provide that behaviour, so passing the test
I refactor, ensuring the tests still pass at the end of the process
I write the next integration test
It seems to me that by definition, my integration tests are testing every model, controller and view that I can create. In reality, am I missing anything by not writing unit tests too?
I'm actually pretty sympathetic to your point of view here. I love Cucumber and I love RSpec -- and I use them both, but not always on the same code. For instance, I rarely write RSpec examples for Rails controllers these days, and I almost never write view specs. Most of my controllers are very similar and don't deviate much from the "stock" controller pattern -- which is already well-tested by Rails's own unit tests. Verifying the same behavior again doesn't gain much for the time it takes and the hassle of mocking all the models. With Cucumber at an integration level I can skip that mocking and get the essential verification I'm looking for. View testing is handled much more transparently in Cucumber most of the time as well. (Then I should see "foo" and so forth.)
But that isn't to say I don't use RSpec extensively in Rails. I use it for the places where my logic lives: models, controller filters, and view helpers. I also have a couple of projects that are almost all business logic, e.g. libraries or API adapters against complex third-party interfaces. For those I usually find myself using RSpec exclusively and skipping Cucumber.
As a heuristic, I'd suggest that you should strongly consider writing a unit test any time any of the following questions can be answered "Yes":
Is the code I'm writing more than trivially complicated?
Does this code exist primarily to give answers to other code?
Is this existing code that I'm refactoring (that doesn't already have a unit test)?
Have I found a bug in this code? (If so, write a unit test before fixing it so it never sneaks in again.)
Do I have to think for more than ten seconds about the most elegant way to implement this code?
Is my Spidey Sense tingling?
If none of the above is true, then maybe you can get away with just doing integration testing. Again, there are a lot of cases where that's reasonable. But if you do run into problems later, be prepared to pay the price -- and that price should include writing unit tests at any moment if they seem called for.
Integration tests are useful to verify that different parts of code are well integrated. They may involve all layers and cover all code but... when an integration test fails, will it tell you where the bug is located? I may be wrong but I don't think so. This will just tell you that there is a problem somewhere. On the other hand, when a real unit tests (written in isolation using mocks or stubs) fails, you know exactly in which unit the problem is located (this is actually the purpose of unit testing, verifying that a unit implements the expected behavior). In other words, unit tests and integration tests are both useful but they have different purposes.
Do you have any rake tasks? Custom capistrano code? Cron methods? An API? Monkeypatches? How about flex or iPhone app integration? A job runner?
In a typical Rails application, there's lots of code that isn't exercised by the HTML UI. So no, unless your app is incredibly simple, webrat tests won't be sufficient.

Best way to add tests to an existing Rails project?

I have a Rails project which I neglected to build tests for (for shame!) and the code base has gotten pretty large. A friend of mine said that RSpec was a pain to use unless you use it from the beginning. Is this true? What would make him say that?
So, considering the available tests suites and the fact that the code base is already there, what would be my best course of action for getting this thing testable? Is it really that much different than doing it from the beginning?
This question came up recently on the RSpec mailing list, and the advice we generally gave was:
Don't bother trying to retro-fit specs to existing, working, code unless you're going to change it - it's exhausting and, unless the code needs to be changed, rather pointless.
Start writing specs for any changes you make from now on. Bug fixes are an especially good opportunity for this.
Try to train yourself into the discipline that before you touch the code, first of all write a failing example (=spec) to drive out the change.
You may find that the design of code which wasn't driven out by code examples or unit tests makes it awkward to write tests or specs for. This is perhaps what your friend was alluding to. You will almost certainly need to learn a few key refactoring techniques to break up dependencies so that you can exercise each class in isolation from your specs. Michael Feathers' excellent book, Working Effectively With Legacy Code has some great material to help you learn this delicate skill.
I'd also encourage you to use the built-in spec:rcov rake task to generate code coverage stats. It's extremely rewarding to watch these numbers go up as you start to get your codebase under test.
Maybe start with the models? They should be testable in isolation, which ought to make them the lowest-hanging fruit.
Then pick a model and start writing tests that say what it does. As you go along, think about other ways to test the code - are there edge cases that maybe you're not sure about? Write the tests and see how the model behaves. As you develop the tests, you may see areas in the code that aren't as clean and de-duplicated (DRY) as they might be. Now you have tests, you can refactor the code, since you know that you're not affecting behaviour. Try not to start improving design until you have tests in place - that way lies madness.
Once you have the models pinned down, move up.
That's one way. Alternatives might be starting with views or controllers, but you may find it easier to start with end-to-end transaction tests andwork your way into smaller and smaller pieces as you go along.
The accepted answer is good advice - although not practical in some instances. I recently was faced with this problem on a few apps of mine because I NEEDED tests for existing code. There simply was no other way around it.
I started off doing all unit tests, then moved onto functionals.
Get in the habit of writing failing tests for any new code, or whenever you're going to change a part of the system. I've found this has helped me gain more knowledge of testing as I go.
Use rcov to measure your progress.
Good luck!
Writing tests for existing code may reveal bugs in your code. These tests will force you to look at the existing code so you can see what test you need to write in order to get it to pass and you may see some code that could possibly be written better, or is now useless.
Another tip is to write a test when you encounter a bug so it should never re-occur, this is called regressional testing.
Retrofitting specs is not inevitably a bad idea. You go from working code to working code with known properties which allows you to understand whether any future change breaks anything. At the moment if you need to make a change how can you know what it will affect?
What people mean when they say that it is hard to add tests/specs to exisitng code is that code which is hard to test is often highly coupled which makes it hard to write low-level isolated tests.
One idea would be to start with full-stack tests using something like the RSpec story runner. You can then work from the 'outside in' isolating what you can in low-level isolated tests and gradually untangle the harder code bit by bit.
You can start writing "characterization tests". With this,you might what to try out the pretentious gem here:
It is still a work in progress though.

Resources