How to test rake task in Minitest? - ruby-on-rails

I have a rake task in my rails application that published jobs to facebook. And then changes some model values. So one way to test the rake task is to invoke the rake task and check values that have been changed.
test 'z' do
# setup some data
Rake::Task['job:publish_to_facebook'].invoke
Rake::Task['job:publish_to_facebook'].reenable
# assert table values that has been changed.
end
But how I can test whether jobs are successfully published on facebook? Is there any better strategy except using capybara and selenium-webdriver ?
Even if i use stubbing and mocking then how can i verify that my jobs are published on facebook?

Most tests should not contact an external API, mainly because it will slow down tests and you might also run into rate limits.
Even if i use stubbing and mocking then how can i verify that my jobs are published on facebook?
The point of stubbing and mocking is precisely not to publish to Facebook. Instead, you would create a class called Facebook (for example) with a method like def post_message(message). This is the app's front door to Facebook, all calls to Facebook go through this class. Then you can use a library like Mocha to overwrite def post_message during testing. You can use it to verify that the application is attempting to post a message, and verify the message itself is correct. It won't actually post the message.
As I mentioned, you do want to make some tests with real calls to Facebook (though not many). These could be in a test like you've shown above, which is an integration test, or it could also be a smaller unit test of the Facebook class I suggested above, which would be a better starting point. For this purpose, you'd want to establish a test account on Facebook. Then your test should clear all messages in the setup and use Facebook's API to verify that the messages were actually posted.

Related

Running integration/acceptance tests on the frontend. Need an API for the frontend to tell Rails which database state to set up for each test

My frontend is an EmberJS-based app. It's totally async in nature, so testing it with Capybara is pain and misery. On the other hand, Ember provides a fantastic test suite out of the box which makes acceptance testing fun and effective.
Normally, either fixtures or backend mocks are used to acceptance-test Ember apps. But testing against mocks does not satisfy me at all:
It will not reveal possible API inconsistencies between the backend and the frontend, race conditions, etc.
It is impossible to test backend business logic this way. Such tests are not integration tests.
Finally, acceptance tests require persistency, so you have to replicate backend behavior in a mock. It's very tedious and you effectively end up implementing two backends.
So I want to test against the real backend! It's trivial to set up Ember to use a local backend instance for testing. But the problem is that the backend will persist its state between individual tests and even test sessions.
That's why I'm thinking of implementing a special public API in Rails:
The API is only available when Rails is run with a specific flag or an env var.
Rails runs in a non-testing mode, serving normal API calls as it would in production.
Before each test, the frontend calls the special API, telling Rails which database setup is needed for this specific test.
When a call to the special API is received, Rails cleans the database and populates it with the requested data. For example, to test item deletion from the cart, the database should have three items in the cart.
Rails finishes the API request, and the frontend starts the test.
Frontend runs test steps, using the normal backend API as it would in production: log in, create posts, comment on them. It will also try doing some forbidden things, e. g. edit posts while not logged in, exceed text length constraints, etc and verify whether the backend rejects forbidden actions.
When the frontend runs next test, it will call the special API again. Rails will discard the state produced by the previous test and set up a new one, for this specific test.
I'm a frontend dev with a sketchy knowledge of Rails. Factory Girl and Database Cleaner seem to be the right tools for the job, but there is absolutely no information how to use them outside Rails' normal test environment. I guess I need a controller or a Rails engine or something.
So the question is: how do I make an API in Rails that can be used by the frontend to tell Rails to set up a certain database state with a fixture factory, while Rails are running in a non-test mode i. e. serving REST API and not running through RSpec/Capybara/Cucumber?
Bonus feature: fixture factory properties should be defined on the frontend so that test code is stored in one place. Thus, the backend should be able to accept fixture factory properties via the special API. Defaults can still be defined in the backend's codebase.
I believe this could become an acceptance/integration testing best practice. If I manage to implement it, I promise to publish a gem.
May be something like this
config/routes.rb
namespace 'test_api' do
resource 'db_transaction', only: [:create, :destroy]
end if Rails.env.test?
controllers/test_api/db_transactions_controller.rb
require 'database_cleaner'
def create
DatabaseCleaner.start
end
def destroy
DatabaseCleaner.clean
end

How to test the view using a test server in Rails?

Currently I've got a couple of files in my view that I'm now beginning to design visually (through CSS) by vising the local web app in my browser. To get to these views, you have to go through an authentication step in my application.
Now when testing the authentication step in a controller, I use a fixture containing some test login credentials. This allows me test other parts of the application after this step. However if I wanted to test using the server, I would have to use real credetials from the database. Am I supposed to put fake data in the 'development' database so I can do this, and instead use real data in the 'production' database?
What you're trying to achieve is called integration testing (or end-to-end testing). Rails provides integration testing out of the box.
Personal preferences : I use Cucumber with the Capybara DSL, and seed data corresponding to my features/scenarios using Cucumber's hooks.
EDIT : at first read, I didn't understand that by "testing" you meant "manual testing". In this case yes, you'd better seed your development database with fake data corresponding to your features/test cases.

How to test a Rails HTTP request to a Sinatra app?

Consider a Rails app that hits a (Sinatra app) API being developed separately from the Rails app. I want to test an API call from within the Rails tests.
The API code:
post '/foo/create' do
...
I created a mock, but that doesn't make sense because it is just a copy of the API file. That stinks.
It is possible to require the API file in the test. But how to call it from RSpec? There is no route in the Rails app for it.
One option is to start the API and make the HTTP call from the Rails test, but this is smelly because:
You have to start the API server to run the Rails tests
Why should a Rails test make a HTTP request? Rack::Test simulates this.
I don't think this will work because the apps have different test databases, but share the same production database.
EDIT: The point of the test is that the API call creates records that the Rails app is expecting. So the Rails app needs to test the state of the database after the API call is made.
Well. The perfect answer for you is a gem to mock the answer like webmock. It will fake a response when acessing that url, so on the test your app will make the requisition as it was for real, only that before it hits the web, it will hit your mock and respond with the desired answer.

How should I deal with online dependencies when running Rspec offline?

I would like to run RSpec to test my code both when I'm connected to the web and when I'm not.
Unfortunately there are some tests in the application which are dependent on having a live connection - email sending, Facebook integration etc.
Is there a best-practice way to create an online/offline testing environment or is this bad practice? Judging by how little I can find about this on the web I'm guessing the latter.
Normally in situations like that you would mock the parts of the code that connect outside your application. This allows you to test against the expected results from the service/system you are connecting to. It's also quicker to run tests.
There's a brief tutorial on mocking with rspec here but I'm sure you can find plenty yourself.
For testing that emails get sent there are other approaches if you are sending through ActionMailer. There's a section on that in the rails testing guide.
EDIT (in response to comment):
You could put a method in TestHelper to only run tests when you are online. Something like:
def when_online
if test_remote_connectivity
yield
else
puts "Skipping test offline."
end
end
Then you can call it like:
def test_facebook
when_online do
.....
end
end
Not sure I entirely advocate it but it might do what you want!
You could use webmock inside the tests/specs you don't want connecting to the remote resource.

Good practices for Integration Tests for my rails app external endpoints?

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 ?
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:
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.
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')
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.
The solution is VCR.
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.
You should write a thin layer around external APIs (Facade/Wrapper) and use the vcr-gem to "stub" network calls.
You can get more information from my article on rails test architecture.

Resources