views:

72

answers:

2

I'll keep it short, I've got a rails app which communicate with other apps, some using SOAP (non-rails apps of course...) and others with REST. I'm making integration tests to ensure that my endpoint wrapper classes have correct mappings and setup. However, they are executed by default by rake test which makes it slow and fragile. I wish to run unit tests frequently and integration tests "on-demand" only. How do you do that?

What're your preferences wrt such integration testing?

  • How deep do you unit test and/or mock?
  • Do you replicate whole SOAP or REST xml responses in stubs?
  • Do you create "external endpoint" integration tests at all?

Update Q: How to exclude a test-dir while running rake test ?

+2  A: 

If you go by what the Rspec/Cucumber folks suggest, then the integration test level is an inappropriate place to mock your data, because in some respects, it defeats the purpose of the integration/acceptance test. However, you have to mock stuff like paypal transactions, right? In my current project, I am facing a lot of this, and here are some of the solutions I am implementing:

  1. Tagging tests that wont work in certain contexts. In my example, lots of servers live behind firewalls and so my tests dont pass if I am at home and not using vpn. So, in cucumber I can tag these as @firewall and tell it to run tests that are not tagged firewall. I'm pretty sure Rspec 2.0 supports this feature as well.
  2. Mocking service requests. Yah, its probably a bad idea, but I am at a loss on how to do it otherwise with any kind of predictability. I have a separate test suite to affirm that the services are running, and from my rails app, i am assuming they are working properly. An example of this would be LDAP. And yes, in these circumstances, I tend to use a real response and do something like. response = double('response') ; response.expects(:data).and_returns('my xml here')

  3. I do think regardless of the complexity of the system that end point tests are really important. I am really enjoying cucumber, it provides me 95% of what I need to do in functional tests, and so I end up writing fewer of these tests and more of the entire workflow tests.

Jed Schneider
after re-reading your question I realize I failed answer the most simple part `rake test` runs all the tests `rake test:integration` only runs the integration tests, while `rake test:units` and `rake test:functionals` do exactly what they sound like they do.
Jed Schneider
Thanks for responding! Yes, I would mock paypal too;) LDAP is a good example usage of an important dependence which you would like to test. In my case the other service (not ldap) is undergoing heavy development, thats why I need the endpoint int-test. Maybe what I'm looking for is another directory ´test/endpoint´ in addition to functionals/units/interation. And exclude it from ´rake test´. Where do you put this "separate test suite" of yours? Btw, I'm using shoulda in this project and don't mind changing to rspec, I like both, but I can do without cucumber regexp layer for now.
Ole Morten Amundsen
Oh and I feel that having to issue several commands like `rake test:units`, `rake test:functionals` in order not to run the `rake test:slow_fragile_int_tests` is a bad workflow. Annotating it `@endpointtest` (example please) and exclude by default is OK, excluding the directory is GREAT, IMO.
Ole Morten Amundsen
It looks like rspec has not implemented tags yet http://github.com/rspec/rspec-core/issues/37, so you would have to go to cucumber for that annotation, ATM. If cucumber is too verbose, you might take a look at steak http://github.com/rspec/rspec-core/issues/37 a carnivorous alternative to cucumber. It may support annotations.
Jed Schneider
For services, I have abstracted the services into its own gem, so I can test the gem separate from the rails app. The gem has its own acceptance testing suite which is basically testing the API for the way I expect the rails code to interface it.
Jed Schneider
Jed, gemifying it is a great idea (separation of concern)! You would get to test the basic send/receive stuff. However, you would miss the mapping of real data to/from domain objects. I'd like to test the entire stack. I guess an embarassing solution could be to have all fast tests in "test/" (shoulda), and the "endpoint integration tests" in "spec/". It would fit my workflow. btw, you didn't paste the steak link.
Ole Morten Amundsen
@ole sorry, here is the steak link: http://github.com/cavalle/steak
Jed Schneider
@ole, yes, in the gem i am using examples -rspec- to assert my expectations on how I want the service to work. perhaps I am missing a critical link.
Jed Schneider
A: 

Excluding endpoint integration tests from rake test and being able to isolate and run them with rake test:endpoints was solved with only a few lines of code. I have to admit though, I spent a whole lot of hours swearing and cursing. There should be more documentation and explanations in the railties source. Ruby code like that tend not to be very self-explanatory, IMO.

Well, here it goes: create your task: lib/tasks/slow_tests.rake

require 'rails/test_unit/railtie'
desc "Runs all endpoint integration tests."
namespace :test do

  #hooks on to the test task through the 'test:prepare'
  #For details, check the railties gem (v3.0+) lib/rails/test_unit/testing.rake,
  #look for "task :test" and "namespace :test"
  TestTaskWithoutDescription.new(:endpoints => 'test:prepare') do |t|
    t.libs << 'test'
    t.pattern = 'test/endpoints/**/*_test.rb'
  end
end

Now I may put my fragile endpoint integration tests in the test/enpoints directory, running them whenever I want (not often)

Note: this supposes test/unit or shoulda.

Ole Morten Amundsen