ActiveRecord data loss when unit testing Rails + Node.js - ruby-on-rails

Background: I am unit testing a game server which is built upon rails 4.1.1 and separate socket.io/node.js for socket messaging. Messages from node.js to rails are going through RESTful http requests.
Single test case runs as follows:
(1) rake unit test --> (2) rails controller --> (3) node.js/socket.io --> (4) rails controller
Problem description: Some DB entries are created with ActiveRecord at step (2), then upon receiving a socket message at step (3) node.js sends HTTP request back to rails controller and finally(!!) at step (4) rails controller tries to access DB entries from step (2), but TEST DB contents are empty at this point.
Question: It seems like desired behavior of rake to cleanup TEST DB, but how can I persist TEST db across test cases and prevent such problem?
Thanks in advance

You should prepare and send request to node app inside a test and assert response there.
But it's not a good practice. The better solution would be HTTP mocks (like webmock gem). This approach will save lots of time in the future.

Luckily, I figured out the solution.
By default, rake is wrapping all tests in separate DB transactions and rolls back on cleanup. Moreover, whatever requests/queries are coming outside of TestCase are not included in transaction and not visible inside the test case.
To avoid such behavior, we have to disable transactional fixtures in test/test_helper.rb
class ActiveSupport::TestCase
self.use_transactional_fixtures = false
end
As downside, we have to cleanup test db manually. So #Alexander Shlenchack points out to avoid such practice in the first place and use http/socket mocks in future.
Here is brief summary http://devblog.avdi.org/2012/08/31/configuring-database_cleaner-with-rails-rspec-capybara-and-selenium/
And related question Rails minitest, database cleaner how to turn use_transactional_fixtures = false

Related

How to get Rspec to use the same DB connection for reader and writer DB in rails 6?

For context, this question arose because we are migration from Rails 5 to Rails 6, and introducing reader / writer database connections via the new replication features.
Our specific problem is with request specs, with an eye towards using transactional fixtures. When we run our request specs files in isolation, they pass. When run as part of a multiple-file pass (such as a full bundle exec parallel_rspec pass used on circle CI) they fail. If we turn off transactional fixtures, the tests take far too long to run, but pass.
Using byebug, we've poked in and determined that the problem is that our test data has been written to / is accessible by the writer DB connection, but the route is attempting to use the reader DB connection to read it. I. E. ActiveRecord::Base.connected_to(role: :reading) { puts Foo.count } is 0, while the same code connecting to writing role is non-zero.
The problem from there seems fairly obvious: because we're using transactional tests / fixtures, the code is never committed to the DB. It's only available on the connection it was made on. The request spec is reading from the 'right' db for the call (a GET request should use the reader db), but in the use-case of tests that's producing errors.
It seems like this is a fairly obvious use case that either Rails or rspec should have a tool for handling, we just don't seem to be able to find the relevant documentation.
You need to tell the test environment that it should be using a single connection for both. There are multiple ways of doing this:
You can configure your test environment not to use replicas at all. See Setting up your application for examples of using a replica and not using a replica then reproduce the non-replica version in your database.yml for the test environment only.
You can use connected_to within your specs themselves so that those tests are forced to use the specific connection you want them to use. One way to do this is with around hooks:
describe "around filter" do
around(:each) do |example|
puts "around each before"
ActiveRecord::Base.connected_to(role: :writing) { example.run }
puts "around each after"
end
it "gets run in order" do
puts "in the example"
end
end
You can monkey patch your ActiveRecord configuration in rails_helper so that it doesn't use replicas (but I'd really recommend #1 over this option)

Outputting HTTP request/response data with RSpec

I have a rails project that serves a JSON API with tests written in RSpec. Often when running specs (request specs, specifically), I’m interested in seeing some details about the HTTP request/response...i.e. the request URL, request body, and response body, ideally JSON pretty-formatted for readability. This isn't for the purposes of documentation but rather as part of the development / debugging process.
I have a helper method I wrote which does this...you just drop a method call into your spec and it prints this stuff out.
But, seems like it would be better if there was a switch that’s part of the running specs. RSpec has custom formatters which I thought might be the right direction, but in trying to build one, I can't figure out how to get access to the request/response objects like you can from inside of your spec.
How can I access the request/response objects in my custom RSpec formatter? Or, perhaps another way to approach the problem?
Here's an approach:
Assuming a rails project, in spec_helper.rb, define a global "after" hook like so:
config.after(:each) do #runs after each example
if ENV['PRINTHTTP']
#use request/response objects here, e.g. puts response.status
end
end
Then, you can conditionally enable by adding the environmental variable on the command-line:
$ PRINTHTTP=1 rspec

Create and populate a test database to run Cucumber tests on

I want to test my Ruby-on-Rails website that uses PostgreSQL database with Cucumber.
I also have the FactoryGirl gem installed, so I can create factories.
I understand that the typical way to create data for the test database would be to write Gherkin tables and put them in the background block of a cucumber file. But I already have a Ruby script that creates samle data suitable for the test database.
Yet, I am currently lost in Cucumber’s settings. Could you please advise how to make Cucumber run my Ruby script populating the test database before each test and how to clean the test database after each test. Apparently, my google-fu is inadequate for this task.
Just:
add the cucumber rule like I have 4 clock in the pocket to features/clock.feature (for example).
implement the rule in the features/step_definition/clock_steps.rb (for example) to create defined amount Clock models, and single Pocket model. and associate them:
When /I have (\d+) clock in the pocket/ do |amount|
pocket = FactoryGirl.create :pocket
amount.times { FactoryGirl.create :clock, pocket: pocket }
end
So, you then will get some data populated.
If you want predefine the step for most scenarios you can either:
Create a larger step (for example) called I have basic clock setup, then define it as an above one, but add another required steps into it.
or
Use step recursion (this is non-recommended way) as follows:
When /I have basic clock setup/ do |amount|
step 'I have 4 clock in the pocket'
end
I ended up using a Before do block in the .rb file with Cucumber tests:
Before do
<code to populate the test database here>
end
Given /first condition/
do some stuff
end
...and so on

How to skip certain tests with Test::Unit

In one of my projects I need to collaborate with several backend systems. Some of them somewhat lacks in documentation, and partly therefore I have some test code that interact with some test servers just to see everything works as expected. However, accessing these servers is quite slow, and therefore I do not want to run these tests every time I run my test suite.
My question is how to deal with a situation where you want to skip certain tests. Currently I use an environment variable 'BACKEND_TEST' and a conditional statement which checks if the variable is set for each test I would like to skip. But sometimes I would like to skip all tests in a test file without having to add an extra row to the beginning of each test.
The tests which have to interact with the test servers are not many, as I use flexmock in other situations. However, you can't mock yourself away from reality.
As you can see from this question's title, I'm using Test::Unit. Additionally, if it makes any difference, the project is a Rails project.
The features referred to in the previous answer include the omit() method and omit_if()
def test_omission
omit('Reason')
# Not reached here
end
And
def test_omission
omit_if("".empty?)
# Not reached here
end
From: http://test-unit.rubyforge.org/test-unit/en/Test/Unit/TestCaseOmissionSupport.html#omit-instance_method
New Features Of Test Unit 2.x suggests that test-unit 2.x (the gem version, not the ruby 1.8 standard library) allows you to omit tests.
I was confused by the following, which still raises an error to the console:
def test_omission
omit('Reason')
# Not reached here
end
You can avoid that by wrapping the code to skip in a block passed to omit:
def test_omission
omit 'Reason' do
# Not reached here
end
end
That actually skips the test as expected, and outputs "Omission: Test Reason" to the console. It's unfortunate that you have to indent existing code to make this work, and I'd be happy to learn of a better way to do it, but this works.

Handling Non-User data in Rails tests

We have a ton of non-user data: 500 product types that haven't changed in 20 years, 200GB of geospatial data that never changes (Queens is always 40.73N/73.82W)... etc. It's the same in Development and Production. It should be the same in Test, but the during testing Rails empties all of the test tables, and it takes a ton of time to re-populate during testing.
What is the Rails way to partition this non-user data so that it doesn't have to be repopulated in Test?
The documentation for this is found in the Fixtures class. (Search for "Transactional fixtures" on that page.)
The example they give starts out like this:
class FooTest < ActiveSupport::TestCase
self.use_transactional_fixtures = true
self.use_instantiated_fixtures = false
...
One of the projects I work on uses a test database with zero fixtures, so we just define this globally in test_helper.rb.
class ActiveSupport::TestCase
self.use_transactional_fixtures = true
self.use_instantiated_fixtures = false
end
jdl shows you the settings to use for enabling and disabling transactional_fixtures, you should be able to set:
# Use self in ActiveSupport::TestCase if you don't have a config block
# in test/test_helper.rb
#
config.use_transactional_fixtures = false
And stop Rails from trying to load fixtures before each run of your tests. The downside is you can't assume all your fixtures are loaded into the DB.
Real Answer
You have too much data in your fixtures. The Railsy thing to do it load only the necessary data in test fixtures; You do not need 200G of geospatial data (what freaking dataset is that anyway? that sounds way too big), and you probably don't need all 500 products.
Tests are for your code, so only include the few geospatial points you need for a test, or only include a few products with unique properties. Keep the DB light enough to be loaded quickly.
Your test fixtures should be separate from you application bootstrapping data, take advantage of that.
Real Real Answer
Don't touch the database in your tests (or, touch it very little). The database is slow and fixtures are difficult or impossible to maintain well. Instead, try using a stubbing framework like Mocha or flexmock. It's faster, and makes your tests read in a clearer manner. Tests are for code, you can stub the database out of the situation and trust that ActiveRecord works (because...it's tested too! :-).
If you do choose to stick with fixtures, I recommend using factory_girl instead of the built in Rails fixtures. You'll have a much better starting point.

Resources