Minitest and setup/teardown hooks - ruby-on-rails

I have the following code in test_helper
require "minitest/spec"
require "minitest/autorun"
require "database_cleaner"
class ActiveSupport::TestCase
DatabaseCleaner.strategy = :deletion
include Minitest::Spec::DSL
setup { DatabaseCleaner.start }
teardown { DatabaseCleaner.clean }
end
And if I write such a test
class MyTest < ActiveSupport::TestCase
test 'test' do
#some code
end
end
setup and teardown are executed
But if I write test like this
class MyTest < ActiveSupport::TestCase
describe 'some test'
before do
#user = FactoryBot.create(:user)
end
it 'first test' do
# some code
end
it 'second test' do
# some code
end
end
end
setup and teardown are not executed. Why? Can I fix it?

Try adding the following to your test_helper.rb:
class Minitest::Spec
before :each do
DatabaseCleaner.start
end
after :each do
DatabaseCleaner.clean
end
end
Or, if you're using minitest-around gem:
class Minitest::Spec
around do |tests|
DatabaseCleaner.cleaning(&tests)
end
end
Important here is the use Minitest::Spec class instead of ActiveSupport::TestCase.
See database cleaner docs for more info.

Related

Rails: How do I ensure that the db reverts to its original state after tests?

I'm trying to write a test that involves creating a new user. However, the test fails all but the first time because the user remains permanently in the db after the initial run.
require 'test_helper'
require 'minitest/autorun'
describe UserMailer < ActionMailer::TestCase do
it 'will send an email when a new user is created' do
original_mail_count = ActionMailer::Base.deliveries.count
User.create(email: "new_user#test.com", password: "password").save
ActionMailer::Base.deliveries.count.must_equal original_mail_count + 1
end
end
How do I ensure that the db returns to its pre-test state after the test is run?
Use database_cleaner gem
Example usage:
# spec_helper.rb
RSpec.configure do |config|
config.before(:each) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end

Rails 4 rspec 3 controller test: session helper module not working for before(:all), works for before(:each)

I'm building a toy chat application using Rails 4.2.7, and am writing specs for my controllers using rspec 3.5. My Api::ChatroomsController requires a user to be logged in in order to create a chatroom, so I have created a Api::SessionsHelper module to create sessions from within the Api::ChatroomsController spec.
# app/helpers/api/sessions_helper.rb
module Api::SessionsHelper
def current_user
User.find_by_session_token(session[:session_token])
end
def create_session(user)
session[:session_token] = user.reset_session_token!
end
def destroy_session(user)
current_user.try(:reset_session_token!)
session[:session_token] = nil
end
end
# spec/controllers/api/chatrooms_controller_spec.rb
require 'rails_helper'
include Api::SessionsHelper
RSpec.describe Api::ChatroomsController, type: :controller do
before(:all) do
DatabaseCleaner.clean
User.create!({username: "test_user", password: "asdfasdf"})
end
user = User.find_by_username("test_user")
context "with valid params" do
done = false
# doesn't work if using a before(:all) hook
before(:each) do
until done do
create_session(user)
post :create, chatroom: { name: "chatroom 1" }
done = true
end
end
let(:chatroom) { Chatroom.find_by({name: "chatroom 1"}) }
let(:chatroom_member) { ChatroomMember.find_by({user_id: user.id, chatroom_id: chatroom.id}) }
it "responds with a successful status code" do
expect(response).to have_http_status(200)
end
it "creates a chatroom in the database" do
expect(chatroom).not_to eq(nil)
end
it "adds the chatroom creator to the ChatroomMember table" do
expect(chatroom_member).not_to eq(nil)
end
end
end
I'm using a before(:each) hook with a boolean variable done to achieve the behavior of a before(:all) hook for creating a single session.
If I use a before(:all) hook, I get the error:
NoMethodError: undefined method `session' for nil:NilClass`
I put a debugger in the create_session method of the Api::SessionsHelper module to check self.class and in both cases, when I use before(:each) and when I use before(:all), the class is:
RSpec::ExampleGroups::ApiChatroomsController::WithValidParams
However when using the before(:each) hook, session is {}, while in the before(:all) hook, session gives the NoMethodError above.
Anybody know what causes this error?
You need to include the helper in the test block:
RSpec.describe Api::ChatroomsController, type: :controller do
include Api::SessionsHelper
end
You can also avoid duplication by including common spec helpers in spec/rails_helper.rb
RSpec.configure do |config|
# ...
config.include Api::SessionsHelper, type: :controller
end
This is also where you should put the database_cleaner config. You should use to clean between every spec not just before all as that will lead to test ordering issues and flapping tests.
require 'capybara/rspec'
#...
RSpec.configure do |config|
config.include Api::SessionsHelper, type: :controller
config.use_transactional_fixtures = false
config.before(:suite) do
if config.use_transactional_fixtures?
raise(<<-MSG)
Delete line `config.use_transactional_fixtures = true` from rails_helper.rb
(or set it to false) to prevent uncommitted transactions being used in
JavaScript-dependent specs.
During testing, the app-under-test that the browser driver connects to
uses a different database connection to the database connection used by
the spec. The app's database connection would not be able to access
uncommitted transaction data setup over the spec's database connection.
MSG
end
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, type: :feature) do
# :rack_test driver's Rack app under test shares database connection
# with the specs, so continue to use transaction strategy for speed.
driver_shares_db_connection_with_specs = Capybara.current_driver == :rack_test
if !driver_shares_db_connection_with_specs
# Driver is probably for an external browser with an app
# under test that does *not* share a database connection with the
# specs, so use truncation strategy.
DatabaseCleaner.strategy = :truncation
end
end
config.before(:each) do
DatabaseCleaner.start
end
config.append_after(:each) do
DatabaseCleaner.clean
end
end

Weird behaviour in Rspec

I am running some test in my controller spec and I found a weird behaviour I cannot explain why this is happen.
My Spec look like this:
require 'rails_helper'
describe BooksController do
let(:user_with_books) { create :user, :with_books }
...
describe 'GET /books/:book_id/owners' do
it 'shows all owners of the book' do
book = user_with_books.books.first
user_2 = create :user
book.owners << user_2
get :owners, book_id: user_with_books.books.first.id
expect(assigns(:users).count).to eq 2
expect(assigns(:users).first).to eq user_with_books
expect(assigns(:users).second).to eq user_2
end
end
end
If I run the command
rspec spec/controllers/books_controller_spec.rb:31 everything is green:
But if I run just rspec, the test will fail!
What is Rspec doing on this spec to change the behaviour? What can I do to fix this issue?
EDIT: My spec_helper.rb
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
config.infer_spec_type_from_file_location!
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
end
According to your spec_helper.rb, you are never calling DatabaseCleaner.clean. It should be called to actually clean the database up in, e. g. before(:each) filter:
config.before(:each) do
DatabaseCleaner.clean
end

how do I test AccountsController < AbstractedResourcesController

My app has a default controller abstracted into a gem (gem 'abstracted') and all my controllers inherits from this abstracted controller.
# Gemfile
gem 'abstracted', path: '../../gems/abstracted'
# app/controllers/accounts_controller.rb
class AccountsController < AbstractResourcesController
end
Now when I write controller (mini)tests for one of this controllers like this:
require "test_helper"
describe AccountsController do
...
I get this error once hitting: rake test from the prompt:
NameError: uninitialized constant AbstractResourcesController
/path_to_rails/projects/cas_server/app/controllers/accounts_controller.rb:1:in `<top (required)>'
My test/test_helper.rb looks like this:
ENV["RAILS_ENV"] = "test"
require File.expand_path('../../config/environment', __FILE__)
require 'minitest/autorun'
require 'action_controller/test_case'
require 'capybara/rails'
require 'rails/test_help'
require 'minitest-rails'
require 'minitest/pride'
# require 'miniskirt'
# require 'factories'
# require 'mocha'
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
# Add more helper methods to be used by all tests here...
end
class ActionController::TestCase
include Devise::TestHelpers
end
class MiniTest::Unit::TestCase
# include MiniTest::ActiveRecordAssertions
# DatabaseCleaner.strategy = :transaction
#
# def setup
# DatabaseCleaner.start
# end
#
# def teardown
# DatabaseCleaner.clean
# end
end
class MiniTest::Spec
include ActiveSupport::Testing::SetupAndTeardown
# alias :method_name :__name__ if defined? :__name__
def build_message(*args)
args[1].gsub(/\?/, '%s') % args[2..-1]
end
end
class ControllerSpec < MiniTest::Spec
include ActionController::TestCase::Behavior
include Devise::TestHelpers
include Rails.application.routes.url_helpers
# Rails 3.2 determines the controller class by matching class names that end in Test
# This overides the #determine_default_controller_class method to allow you use Controller
# class names in your describe argument
# cf: https://github.com/rawongithub/minitest-rails/blob/gemspec/lib/minitest/rails/controller.rb
def self.determine_default_controller_class(name)
if name.match(/.*(?:^|::)(\w+Controller)/)
$1.safe_constantize
else
super(name)
end
end
before do
#controller = self.class.name.match(/((.*)Controller)/)[1].constantize.new
#routes = Rails.application.routes
end
subject do
#controller
end
end
# Functional tests = describe ***Controller
MiniTest::Spec.register_spec_type( /Controller$/, ControllerSpec )
Add this in your controller spec file before describe AccountsController do,
module AbstractResourcesController
class Base
def self.protect_from_forgery; end
end
end
require 'abstract_resources_controller'

Rails MiniTest 'post' method not found

I'm receiving the following error when I try to run my request specs:
POST :: /users/:id/authentications request::successful request#test_0001_Adds an authentication record to a user:
NoMethodError: undefined method `post' for #<#<Class:0x007fa607163028>:0x007fa6070012c0>
test/requests/authentications_test.rb:9:in `block (3 levels) in <main>'
Here's the test itself:
require "minitest_helper"
describe "POST :: /users/:id/authentications request" do
describe "successful request" do
it "Adds an authentication record to a user" do
user = create_user
post user_authentications_path(user)
response.status.must_equal "200"
end
end
end
Here's the minitest_helper.rb file:
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "rails/test_help"
require "minitest/autorun"
require "minitest/rails"
require "minitest/rails/capybara"
Dir[Rails.root.join("test/support/**/*.rb")].each {|f| require f}
class ActiveSupport::TestCase
end
# database cleaner
DatabaseCleaner.strategy = :transaction
class MiniTest::Spec
before :each do
DatabaseCleaner.start
end
after :each do
DatabaseCleaner.clean
end
end
class RequestTest < MiniTest::Spec
include Rails.application.routes.url_helpers
register_spec_type(/request$/, self)
end
Relevant versions of things:
Rails: 3.2.13
minitest-rails: 0.9.2
minitest-rails-capybara: 0.9.0
It really doesn't make sense that I can't call post. It looks like every other example out the web can do it just fine.
Any help with this is greatly appreciated.
You have a lot going on in your test helper. It seems that you have copied several different approaches to running minitest in your rails tests. I suggest the following:
Remove the following from your test helper:
# database cleaner
DatabaseCleaner.strategy = :transaction
class MiniTest::Spec
before :each do
DatabaseCleaner.start
end
after :each do
DatabaseCleaner.clean
end
end
class RequestTest < MiniTest::Spec
include Rails.application.routes.url_helpers
register_spec_type(/request$/, self)
end
Add the following to your test helper:
class ActionDispatch::IntegrationTest
# Register "request" tests to be handled by IntegrationTest
register_spec_type(/Request( ?Test)?\z/i, self)
end

Resources