Why does my session expire when using PerformanceTest and not IntegrationTest? - ruby-on-rails

OK, I am writing performance tests and am having trouble getting my session to persist like it does in integration tests. As I understand it, PerformanceTest is a child of IntegrationTest and any integration tests should work with performance test. However, when I take a integration test and copy it over to performance, change the ActionController::IntegrationTest to ActionController::PerformanceTest and then run the test, it fails.
I am using Authlogic and have not had a problem with the integration test sessions sticking around. With the performance tests though it looks like the session gets created properly but when I visit the "/reports" page (which is a protected page) it redirects me to the login page like there is no user session at all.
require 'performance_test_help'
class SimpleTest < ActionController::PerformanceTest
setup :activate_authlogic
test "login" do
assert user_session = UserSession.create!(User.find_by_login("admin"))
get "/reports"
assert_response :success
end
end
What's going on here? I've tried multiple ways to get a user session (create, post, etc.) and nothing seems to work. This is the first time I've written performance tests so I'm probably doing something stupid...
BTW: I am running Ruby 1.8.7, Rails 2.2.2 on Debian Squeeze.

You have to setup your performance tests like your integration tests.
Try to login using post:
post "user_session", :user_session => {:login => "user", :password => "password"}

not sure what is in your setup there, but you are missing require 'test_helper' as well. If this method is in there, or in an Authlogic test helper, you may have to make sure it's included.

Related

Websockets flaky tests after upgrading to Rails 6

While upgrading a middle size app from Rails 5.1 to 6.0.2.2 (+ Ruby 2.6.1 -> 2.6.3) I start to get flaky tests in all kind of tests. 90% coverage, 500 hundreds tests, and between 0 to 8 tests failings, totally random. After a long bug hunt, I noticed that I got everything working with 100% confidence if I skip all Websocket related tests.
This is typically what I have to test and how I'm doing it (Minitest/Spec syntaxe):
class Api::PlayersControllerTest < ActionController::TestCase
before do
#user = users(:admin)
sign_in #user
end
it "broadcasts stop to user's player" do
put :update, format: :json, params: {id: #user.id}
assert_broadcast_on("PlayersChannel_#{#user.id}", action: "stop")
end
end
Notice that it's not an "integration test" because we're using a raw API call. What I have to check is: if some request is coming to some controller, ActionCable is broadcasting a Websocket message. That's why I have to use Devise sign_in helper in a controller Test.
ActionCable is backed by Redis, in all environments.
I do not use Parallel testing.
Dataset is using Fixtures, not factories.
use_transactional_tests is set to true
I have 23 tests like this one, and they all used to pass without any problem using Rails 5.1. Started one by one using a focus, they also all pass 100% both in Rails 5 or 6. The problem is when executing the whole test suite, I start to get flakiness in all sections (Unit/Models tests included), mostly related to dataset consistency. Actually, it looks like fixtures are not (or poorly) reloaded.
Any ideas? Is it something wrong with what I'm doing, or do you think it's a Rails 6 issue?
Ok, solved by adding Database Cleaner Gem. Pretty curious that it works using the same ":transaction" strategy that's in use with the Rails barebone fixture management... but anyway, it works!

Handle a response code in test without polluting ApplicationController

My problem is that we're receiving mysterious 504 errors in SolanoCI, and I'm trying to find a clean way to handle them only in the test environment without adding test-specific code to our ApplicationController.
I've tried stubbing a default handler via the test setup like so:
allow(ApplicationController).to receive(:timeout).and_return {
#do a bunch of stuff to try to pull out request information
}
but I'm finding that it's lacking some important information that's accessible, like the original request url and any stacktrace information.
Any suggestions are welcome.
I think the easiest way for you in this situation would be to ask Solano to run a debugging session on their box for you (they give you ssh access to their server, so you may run tests yourself).
This is a real time saver in a black box situations like this. You may run tests multiple times and use all sorts of debugging tools there. You may have to install them first, since Solano tries not to install extra tools from your Gemfile (such as pry) to minimize test's running time.
Just write them via a contact form and they will give you an access. Don't forget to stop that session after you've done with it, since the time it runs is deducted from worker hours in your plan.
Hope it helps.
EDIT: It seems Anonymous controller feature is perfect for your case:
Use the controller method to define an anonymous controller that will
inherit from the described class. This is useful for specifying
behavior like global error handling.
So, you can do something like this:
RSpec.describe ApplicationController, :type => :controller do
controller(ApplicationController) do
rescue_from Timeout::Error, :with => :log_timeout
def log_timeout(error)
# log error as you wish
end
end
describe "handling timeout exceptions" do
it "writes some logs" do
get :index
expect(response).to have_https_status(504)
end
end
end

Handle authentication with Capybara / Minitest for integration testing

I'm stuck trying to create integration tests using Capybara and MiniTest::Spec. I'm not using any 3rd party plugin for authentication. I'm using basic Authentication using has_secure_password built into rails 4.1
I have a helper that is looking for current_user which is created after authentication (pretty standard).
I've tried authenticating with Capybara then testing with visit:
test.rb
require 'test_helper'
describe "Admin area integration" do
setup do
def current_user
create(:admin_user, password: "test", password_confirmation: "test")
end
end
teardown do
current_user.destroy!
end
# results in error below
it "visits admin area path" do
visit admin_area_path
page.text.must_include('Dashboard')
end
# test passes
it "test user login" do
visit "/login"
within("#login_form") do
fill_in('email', with: current_user.email)
fill_in('password', with: "test")
end
click_button('login')
has_content?('Welcome')
end
end
Error
undefined method `email' for nil:NilClass app/helpers/application_helper.rb
Is there a way to pass the current_user object using capybara visit or am I missing something simple so the helper will not throw an error?
You are not supposed to modify internals of your Rails app, when doing integration tests. These tests should simulate the real world behaviour - a user visiting your site with a browser. So there is no way to pass the current_user object to capybara, like there is no way to modify the user session for your user from outside the app.
The straightforward way would be extracting the login steps(filling out the form) into separate function within some other test file( we usually have them all in test/support/** and just require all supporting functions in spec_helper). Then you repeat the login steps before any other test, which requires the user to be logged in.
However once we have tested the login, we can rely on it and the repetitive task of login the user each time can become quite annoying. It wouldn't be Ruby otherwise, when there wasn't a way to patch your app behaviour, while in test mode.
You can try using some mocking/stubbing lib and just stub the current_user method on any instance of the class which is holding it. Mocha example:
require 'mocha'
ApplicationController.any_instance.stubs(:current_user).returns(User.new {...})
The other option would be to modify the rack session directly. I expect your are storing the user_id in the session, and your current_user method just loads the user with that id.
So you can just require the rack_session_accessgem within your testsuite and set the user_id of your test user.
Remember also to disable transactional fixtures at least for the integration tests and use database_cleaner instead. Otherwise capybara will not be able to see any of your test data created, because it will be in an uncommitted transaction which is only accessible for the initiating thread.
See Configuring database_cleaner with Rails, RSpec, Capybara, and Selenium

What is happening during Rails Testing?

I'm new to Ruby On Rails. I love, it has Testing capabilities built in. But, I can't wrap around my head with testing. Here is my first basic Question about it.
What happens during testing really?
I understand development, we want some result, we use the data we have or get it from users to achieve the end result we want. But, the notion of testing seems sometimes confusing for me. I have been testing applications in browser for some time, are we replicating the same with code? Is it what testing is about? Replicating browser testing with automated code? Enlighten Me here.
Reading A Guide to Testing Rails Applications will be a good starting point.
Basically, you have three kinds of tests: unit, functional and integration.
Unit tests are testing your Models. In these tests you check whether a single method of your model works as expected, for example you set assign a login with spaces, and then you test whether the spaces were removed:
class UserTest < ActiveSupport::TestCase
def test_login_cleaning
u = User.new
u.login = " login_with_spaces "
assert_equal "login_with_spaces", u.login
end
# ... and other tests
end
Functional tests are testing your controllers (and views). In each test you simulate one request sent to one controller with given set of parameters, and then you ensure that the controller returned the proper response.
Note however, that in this test you cannot test the rendering of the page, so it's not strictly simulating a browser. To test whether your page looks nicely, you need to do it manually (I am almost sure some techniques exist, but I do not know of them).
An example of functional test:
class UserControllerTest < ActionController::TestCase
def test_show_renders_admin
get :show, :id => 1
assert_response :success
assert_select "div.user" do
assert_select "span.name", "Joe Admin"
end
end
def test_show_handles_unknown_id
get :show, :id => 9999
assert_response 404
assert_select "p.warning", "No such user"
end
end
Integration tests are testing a sequence of requests - something like a scenario, where an user logins, gets the 'create user' page, creates an user, and so on. These tests check whether the single requests (tested in functional tests) are able to work together.
I see that Simone already pointed the importance of automation in tests, so the link to the Guide is the only value in my answer ;-)
You may find it very helpful to apply some rules of Test Driven Development, especially when your project matures a little.
I know that it's not easy to start the project by writing test, because often you do not yet know how everything will work, but later, when you find a bug, I strongly suggest to start fixing every bug from writing a failing test case. It really, really helps both in the bug-fixing phase, and later - ensuring that the bug does not reappear.
Well, I noticed that I did not directly answer your question ;-)
When you start test procedure, Rails:
deletes the test database (so make sure you do not have any valuable data here),
recreates it using the structure of the development database (so, make sure you have run all your migrations),
loads all the fixtures (from test/fixtures/*)
loads all the test classes from test/units/* and other directories,
calls every method whose name starts with 'test_' or was created by the macro test "should something.." (alphabetically, but you may consider the order as being random)
before every call it executes a special setup procedure, and after every call it executes teardown procedure,
before every call it may (depending on the configuration) recreate your database data, loading the fixtures again.
You will find more information in the Guide.
What happens during testing is that you really run a set of specialized programs or routines (test code) that calls routines in your application (code under test) and verifies that they produce the expected results. The testing framework usually has some mechanism to make sure that each test routine is independent of the other tests. In other words the result from one test does not affect the result of the others.
In Rails specifically you run the tests using the rake test command line tool. This will load and execute each test routine in a random order, and tell you if each test was successful or not.
This answer doesn't necessary apply to Rails itself. When you talk about testing in Rails, you usually mean automatic testing.
The word automatic is the essence of the meaning. This is in fact the biggest difference between unit testing and "browser" testing.
With unit testing you essentially write a code, a routine, that stresses a specific portion of your code to make sure it works as expected. The main advantages of unit testing compared to "browser" testing are:
It's automatic and can be run programmatically.
Your test suite increases during the development lifecycle.
You reduce the risk of regression bugs, because when you modify a piece of code and you run the test suite, you are actually running all the tests, not just a random check.
Here's a basic, very simple example. Take a model, let's say the User model. You have the following attributes: first_name, last_name. You want a method called name to return the first and last name, if they exist.
Here's the method
class User
def name
[first_name, last_name].reject(&:blank?).join(" ")
end
end
and here's the corresponding unit test.
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def test_name
assert_equal "John Doe", User.new(:first_name => "John", :last_name => "Doe").name
assert_equal "John", User.new(:first_name => "John").name
assert_equal "Doe", User.new(:last_name => "Doe").name
assert_equal "", User.new().name
end
end

Tests pass using "autotest" but not "rake test" using Authlogic

My tests fail when doing "rake test:functionals" but they pass consistently using autotest.
The failing tests in question seems to be related to Authlogic not logging in the user properly when using rake.
For facilitating signing in a user in tests, I have a test helper method as follows:
class ActionController::TestCase
def signin(user, role = nil)
activate_authlogic
UserSession.create(user)
user.has_role!(role) if role
end
end
The above method is used to signin a user
My stack is shoulda/authlogic/acl9/factory_girl/mocha
The reason why I suspect Authlogic being the issue is the failing tests look like this:
54) Failure:
test: A logged in user PUT :update with valid data should redirect to user profile. (UsersControllerTest)
[/var/lib/gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/action_controller/macros.rb:202:in `__bind_1251895098_871629'
/var/lib/gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `call'
/var/lib/gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `test: A logged in user PUT :update with valid data should redirect to user profile. ']:
Expected response to be a redirect to <http://test.host/users/92> but was a redirect to <http://test.host/signin>.
55) Failure:
test: A logged in user PUT :update with valid data should set the flash to /updated successfully/i. (UsersControllerTest)
[/var/lib/gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/assertions.rb:55:in `assert_accepts'
/var/lib/gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/action_controller/macros.rb:41:in `__bind_1251895098_935749'
/var/lib/gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `call'
/var/lib/gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `test: A logged in user PUT :update with valid data should set the flash to /updated successfully/i. ']:
Expected the flash to be set to /updated successfully/i, but was {:error=>"You must be signed in to access this page"}
Autotest reads all test files upfront AFAIR (it does so with RSpec, I haven't been using plain tests for a long time now so I may be wrong).
To properly test controllers you need to call activate_authlogic in your setUp method. This is probably done automatically (globally) for integration tests.
Since autotest reads all tests it runs this global setUp and functional tests pass. When you run only functional tests authlogic is not enabled and your tests fail.
I'm not sure about where your problem lies, but I suggest you use Cucumber for testing controllers and user interaction instead of unit tests/rspec. The reason for that is that you exercise your entire app, including the authentication and authorization code you have.
Clearly the user is not getting logged in. Seems like Bragi Ragnarson might be on to something.
Here are some other things to isolate the problem:
Understand if the test is incomplete or relying on some side-effect of autotest. Run the test by itself:
ruby test/functionals/users_controllers_test.rb
Presumably that won't work. If it doesn't, there's some global code that is getting invoked for non functional tests by autotest. It's probably code setting in the test/integration or test/units directories, or one of their requires there.

Resources