How to test the view using a test server in Rails? - ruby-on-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.

Related

Good approach to switch database for Test mode / Sand box mode : Rails 6

I am working on an app where I now need to add test mode or sandbox mode features like Stripe provides in the dashboard. In this feature, when the user turned the test mode on, the user can test the functionality and can create dummy data in the same login session/token.
I have tried to use the Rails 6 feature to use multiple databases but have a few questions:
is it good to switch the connection in production for test mode even we will not have many test requests?
will it be good to have a separate instance for test mode with the test subdomain? in this case how we should manage the login sessions? should we copy data to the test the database? will it be good and common practice?
I only can manage to implement this if I have user data in the test database so that when I switch database connection system will not send an unauthenticated response.
Note:
for login and user update, it will use the primary database all time and for other actions, it will use the test_mode database. I am doing this by skip_around_action in a specific controller.
We are doing this so that when the user turned off test mode, it will update the primary database and the next request will use the primary database as per around_action logic
Here is my current code in application_controller.rb:
around_action :setup_db_connection
def setup_db_connection
database_key = (user.test_mode?) ? :test_mode : :primary
ActiveRecord::Base.connected_to(database: database_key) do
yield
end
end
test_mode database key has test database configuration in database.yml and similarly for primary database key. Both are completely two different databases.
Can anyone please tell me if I am going in to correct direction? Any help or thought will be appriciated. Thank you in advance!!!!
You should just create an additional environment as this is the exact problem that they are designed to solve.
Rails just ships preconfigured with development, test and production as thats the bare minimum you can get by with. You can actually have an unlimeted number of environments.
So lets say you want to create an enviroment named staging which closely matches your production environment. You can start by copying the productions settings:
cp app/environments/production.rb app/environments/staging.rb
And then just setup an additional hash in your database.yml and credentials files.
staging:
<<: *default
database: my_app_staging
You can then set the environment by using the RAILS_ENV env var or the command line arguments when starting the Rails server. Your staging environment can be run for example in a separate Heroku application made available on a subdomain. If you're using Heroku or some other SAAS platform a hobby tier will often be sufficient for staging.
When it comes to data you can either work on a set of dummy data generated by wiping and seeding or regularily wipe the staging database with a backup from your production database.
I would not consider using a "test mode switch" a good idea as it makes far to easy to inadvertantly mess things up. Using a separate environment lets you use a completely different set of credentials as well so you won't accentially do something like create a real credit card charge or destroy data on third party services. Sandboxes should be isolated.

How to test rake task in Minitest?

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.

Setting up test data for full-stack testing of a single page web application and its back-end

Short version of my question:
In Cucumber tests written for an Angular single page web application, how do I accomplish the tasks usually performed in the "given" section of a scenario (such as setting up test data, defining database record associations, and ensuring a clean database state between tests) when testing the full-stack, both the front-end application and its back-end? The application source code is stored in two separate Git repositories, one for the front-end application and one for the back-end. The back-end is written in Ruby using the Rails API gem. The challenge in testing this application full-stack comes from the fact that it's actually two applications, in contrast with a more traditional Ruby on Rails application not implemented as a single page application.
Full version of my question:
I'm looking to write a series of Cucumber tests for a web application. The application consists of a front-end single page application written in Angular, and a back-end API written using Rails API. The source code for the front-end and the source code for the back-end each reside in their own Git repositories, providing a clean separation between the two codebases. Furthermore, the application uses MySQL and Elasticsearch.
I've used Cucumber in the past on previous Ruby on Rails projects. These projects were not developed as single page applications. In these projects it was easy to create Ruby objects as test data in Cucumber step definitions. For example, consider the following feature file from a Rails project that was not a single page application:
# features/home_page.feature
Feature: Home page
Scenario: Viewing application's home page
Given there's a post titled "My first" with "Hello, BDD world!" content
When I am on the homepage
Then I should see the "My first" post
The steps in this feature file can be implemented with the following step definitions:
# features/step_definitions/home_page_steps.rb
Given(/^there's a post titled "(.*?)" with "(.*?)" content$/) do |title, content|
#post = FactoryGirl.create(:post, title: title, content: content)
end
When(/^I am on the homepage$/) do
visit root_path
end
Then(/^I should see the "(.*?)" post$/) do |title|
#post = Post.find_by_title(title)
page.should have_content(#post.title)summary of the question
page.should have_content(#post.content)
end
In Ruby on Rails projects that were not developed as single page applications, testing tools can be included into the project as Ruby gems. For me, these tools would include:
group :test do
gem 'shoulda-matchers'
gem 'cucumber-rails', require: false
gem 'database_cleaner'
gem 'selenium-webdriver'
end
group :development, :test do
gem 'factory_girl_rails'
end
As you can see, this includes Factory Girl, used for setting up Ruby objects as test data and defining database record associations, and Database Cleaner, used to ensure a clean database state between tests. The inclusion of Selenium WebDriver is required for Cucumber scenarios which use JavaScript.
The situation is different in the case of my single page application. As described above, the application is broken into two separate code bases, one for the Angular front-end single page application and the other for the Rails API back-end interface.
However, single page applications such as my project still have the same testing requirements as more traditional Rails applications not built as single page applications. It's necessary to test the application full-stack to ensure each component, both front and backend, work together as expected. There will need to be Cucumber steps defined which create the "given" preconditions prior to a test, and it will be necessary to ensure a clean database between tests.
How do I test with Cucumber an application such as this, one with two code bases, implemented as a single page application? There's a version of Cucumber available for JavaScript called CucumberJS. However, I don't know how to create fixtures, record associations, and to ensure a clean database between tests using CucumberJS. There's also a tool for testing JavaScript written with Angular called Protractor. I imagine this tool taking the place of Selenium WebDriver.
Answer to the short version question.
The challenge in testing this application full-stack comes from the fact that it's actually two applications
What you describe is tipical to single page apps. I have a number of spa examples (covered with E2E) where the backend is a RESTful API in PHP, Dot.net or ever a cloud web-service (parse.com). To my point of view, it is best to treat such apps, to qoute you agin, as 2 different apps and write 2 different sets of test for each. I believe that is the only way to write stable, scalable and fast e2e tests.
Basically what we do is create a local non-persistent mock api (in javascript) especially for testing. Here's a list of requirements/guidelines for such an api:
Provides mock data for the test suit (read - it is coupled with test suit)
Covers just enough methods to make the tests pass.
It is non-peristent (request parameters is all there is to make a response, though there can be exceptions to that). All persistance is on the client (localstorage, cookies). It is important, because simply be clearing client storage and refreshing the page - you get the fresh state of you app! Where you to test a monolith - you'd have to clear and repopulate the database, which is a hume waste of time and completely non-scalable.
Embedded into the angular repository, it is a part of a testing suit and a development process.
How does that look in practice.
Let's say we are testing a Login feature of the website and it has 3 scenarios:
var homepage = require('pages/homepage'); //page object for home page
describe('Authentication', function () {
beforeEach(function () {
browser.manage().deleteAllCookies(); //clear persistance
browser.refresh(); //refresh page
homepage.get(); //go to homepage
});
it('should show error when user enters wrong credentials', function () {
homepage.signIn('admin', '123'); //submit invalid credentials
expect(browser.getCurrentUrl()).toMatch('/home$');
expect(homepage.getSignInForm()).toContain('wrong credentials');
});
});
To satisfy these tests all we have to do is implement an api for the POST /login request:
router.get('/login', function (req, res) {
var username = req.body.username, password = req.body.password;
if (password !== 'admin') {
res.status(404).json({"error": "wrong credentials"});
} else {
res.json(User(username}));
}
});

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

Mantain session between tests using Geb

I'm testing my application using Geb, and I want to mantain session between tests so I can avoid to log in in every tests (this is annoying when watching the tests in the browser).
Is there a way to mantain the session?
By default Geb test integrations clear all the cookies after every test which means that you loose your web sessions. You can easily change that behaviour by using the following configuration option in your GebConfig.groovy:
autoClearCookies = false
You can read more about using configuration here.
So yes, it is possible to maintain session between tests.
If you are using Spock, one option that you can do is to structure your "features" (test methods) in a linear fashion and use the #Stepwise annotation on the class. This will ensure that the cookies and browser object are not reset/replaced between features/test-methods
Yup, it isnt possible now. My specs start by logging in and finish by logging out.

Resources