I decided to try using simplecov gem, and I think it's a cool tool, but I have one problem:
I have a model User, and I have user_spec.rb which contains test cases, but simplecov shows 0% coverage of this model. And it shows 100% coverage for other models, which is true. I don't understand what's the issue with the User model.
class User < ActiveRecord::Base
extend Enumerize
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
STATUS_ACTIVE = :active
STATUS_BANNED = :banned
enumerize :status, in: [STATUS_ACTIVE, STATUS_BANNED], default: STATUS_ACTIVE
with_options inverse_of: :user, dependent: :destroy do
has_one :profile
has_many :articles
end
before_create :build_default_profile
private
def build_default_profile
build_profile
end
end
user_spec.rb
require 'rails_helper'
RSpec.describe User, type: :model do
describe '#validations' do
it { should have_one(:profile).dependent(:destroy) }
it { should validate_presence_of(:email) }
it { should validate_presence_of(:password) }
it { should validate_confirmation_of(:password) }
it { should enumerize(:status).in(User::STATUS_ACTIVE, User::STATUS_BANNED).with_default(User::STATUS_ACTIVE) }
#TODO other devise validations
end
describe '#callbacks' do
it 'creates profile after_create' do
user = build(:user)
expect(user.profile).to be_nil
user.save
expect(user.profile).to be_a(Profile)
end
it 'must not create profile after update' do
user = create(:user)
profile = user.profile
user.email = Faker::Internet.email
user.save
expect(profile.id).to eq(Profile.find_by(user_id: user.id).id)
end
end
end
coverage
File % covered Lines Relevant Lines Lines covered Lines missed Avg. Hits / Line
app/models/user.rb 0.0 % 28 28 0 28 0.0
app/models/admin.rb 100.0 % 3 1 1 0 1.0
app/models/article.rb 100.0 % 32 19 19 0 5.8
app/models/profile.rb 100.0 % 13 6 6 0 1.0
Make sure that you are starting SimpleCov correctly. In your case,
Load and launch SimpleCov at the very top of your rails_helper.rb
See more: https://github.com/colszowka/simplecov#getting-started
It happens with me only when I use spring, actually when I use rspec binstub generated by spring-commands-rspec gem. Try to stop spring with command spring stop and run specs again with rspec spec.
I have a similar issue. I have the current simplecov 0.17.1.
I'm using Rails 6 with the default setup (Minitest and Spring, no rspec), I run my tests with rails test.
I have try all the other answers without success.
simplecov may be buggy: https://github.com/colszowka/simplecov/issues/671
I'm trying alternative like fastcov
edit1
fastcov seems to be a ligthen copy of simplecov, not mature at all. It's not released yet! Is their any alternative to simplecov?!
edit2
I manage to make it work by adding to the top of bin/rails
#!/usr/bin/env ruby
if ENV['RAILS_ENV'] == 'test'
require 'simplecov'
SimpleCov.start 'rails'
puts "required simplecov"
end
# ...
AND in test_helper.rb, I set parallelize(workers: 1)
# test/test_helper.rb
require 'simplecov'
SimpleCov.start 'rails'
ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
require 'rails/test_help'
class ActiveSupport::TestCase
parallelize(workers: 1)
fixtures :all
end
I run tests with the command RAILS_ENV=test rails test
You have to create an initilizer like this:
config/initializers/simplecov.rb
if ENV['RAILS_ENV'] == 'test'
require 'simplecov'
SimpleCov.start 'rails'
puts "required simplecov"
end
I had the same problem and just found the answer here: https://github.com/colszowka/simplecov/issues/82
The require should be happening before loading anything else. In my case I had:
require simplecov
SimpleCov.start 'rails'
after:
require File.expand_path('../../config/environment', __FILE__)
which probably made the devise modules not being loaded. As soon as I moved the "require simplecov" and "simplecov.start" to the very beginning of rails_helper, it worked as expected.
The metric that simplecov displays is the number of lines that get called in the process of running test cases. For example if I had:
class Test
def method
'Response'
end
end
RSpec.describe Test, type: :model do
context '#method' do
let(:test) { Test.new }
it 'returns response' do
expect(test.method).to eq('Response')
end
end
end
simplecov will show 100% coverage because it is hitting every single line in the Test class when I run my specs. In the case of your user class, your specs don't actually invoke any lines in the user class because you don't have any relevant lines (it isn't considering your private method to be relevant).
I wouldn't worry about the 0% coverage for your user model as the tests you have seem pretty comprehensive.
I was seeing the same issue, and I think it has something to do with Spring rspec binstubs. I'm using the spring-commands-rspec gem and have a binstub for rspec in bin/spring. After creating that binstub, my Simplecov test coverage calculations went down by 10% and showed that my User model had 0% coverage. When I deleted (or renaming works too) the bin/spring script and re-ran rspec, my coverage was back up.
Are you using spring-commands-rspec or any other Spring binstubs to run your tests? I'll post more once I figure out if there's a workaround.
Related
Using
Ruby "2.7.1"
Rails "6.0.3.1"
Mac OS Catalina 10.15.5
Using the default Rails Minitest and FactoryBot for test data.
Issue: I have test data being shared between system test files and hence failing tests. When I run the system test files individually the tests pass but this is inconvenient.
I use the the system test config out of the box as in the application_system_test_case.rb
This is my system test scenario.
I have 3 Customers in customers_system_test.rb file and 1 Customer (so far) in job_description_system_test.rb Customer 1 appears in the customers_system_test.rb failing test screenshots that Rails conveniently provide.
Note: The 3 failing tests all concern record counts such as assert_equal Customer.count, 3
The 3 failing tests
test "visit create customer and navigate back" do
...
assert_equal Customer.count, 3
end
test "cancel destroying a customer" do
assert_equal Customer.count, 3
...
end
test "destroy a customer" do
assert_equal Customer.count, 3
...
end
Interestingly I had the same issue with test data not being cleaned up when running rails test for controller, integration tests etc: The way I get around that is adding the following snippet to the test_helper.rb :
(ActiveRecord::Base.connection.tables - %w{schema_migrations}).each do |table_name|
ActiveRecord::Base.connection.execute "TRUNCATE TABLE #{table_name} CASCADE;"
end Doesn't work for system tests
I feel like I am missing the obvious?
test_helper.rb
require "simplecov"
SimpleCov.start "rails"
ENV["RAILS_ENV"] ||= "test"
require_relative "../config/environment"
require "rails/test_help"
class ActiveSupport::TestCase
include FactoryBot::Syntax::Methods
include Warden::Test::Helpers
# Run tests in parallel with specified workers
# parallelize(workers: :number_of_processors)
parallelize(workers: 1)
# 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...
include Devise::Test::IntegrationHelpers
end
(ActiveRecord::Base.connection.tables - %w{schema_migrations}).each do |table_name|
ActiveRecord::Base.connection.execute "TRUNCATE TABLE #{table_name} CASCADE;"
end
Ok I got all tests running and passing including system tests by doing the following (wasn't aware this was necessary in Rails 6)
...
setup do
sign_in user
#customer_1 = FactoryBot.create(:customer_with_job_descriptions)
#job_description_2 = FactoryBot.create(:job_description, customer_id: #customer_1.id)
end
teardown do
#customer_1.destroy!
#job_description_2.destroy!
end
...
Ref: 8 Setup and Teardown
https://guides.rubyonrails.org/v3.2/testing.html#setup-and-teardown
........................................................................................
Finished in 22.971069s, 4.3968 runs/s, 21.4182 assertions/s.
101 runs, 492 assertions, 0 failures, 0 errors, 0 skips
Rails 6.0.0.beta3
rspec 3.8.0
I've just installed rspec-rails on my Rails app. I ran rails g rspec:install and it generated spec/spec_helper.rb, spec/rails_helper.rb and .rspec
The only thing I changed was uncommenting the suggested settings in spec_helper
I'm using gruf to run a gRPC server, instead of a normal HTTP server.
I've also installed gruf-rspec intending to use that to test my gruf controllers.
My gruf controller is at app/rpc/controllers/users_controller.rb following the gruf documentation. Also the compiled protobuf file is at app/rpc/users_services_pb.rb
This is the class signature of the controller:
require 'users_services_pb'
require_relative 'permission_helper'
class UsersController < Gruf::Controllers::Base
bind ::Sil::Rev79::Users::Service
...
end
My problem is that in my test the described_class is nil
Here is my test
# spec/rpc/users_contollers_spec.rb
require 'rails_helper'
require 'users_services_pb'
RSpec.describe 'UsersController' do
describe 'list_users' do
it 'succeeds' do
expect(described_class).not_to be_nil
end
end
end
The test fails.
Why is described_class nil and how can I fix this?
Remove the quotation marks. It shouldn't be a string.
Rspec.describe UsersController do
# insert code
end
I followed codeschool tutorial, but I encountered some troubles.
Here is zombie_spec.rb
#spec/model/zombie_spec.rb
require 'spec_helper'
require 'zombie'
describe Zombie do
it 'is invalid without a name' do
zombie = Zombie.new
zombie.should_not be_valid
end
end
zombie.rb
#spec/zombie.rb
class Zombie < ActiveRecord::Base
validates :name, presence: true
...
end
After I typed rspec spec/models/zombie_spec.rb, it throw uninitialized constant ActiveRecord (NameError)
I've put this project on github
I think the tutorial might be trying to transition from using RSpec on a plain Ruby object to using the rspec-rails gem on an ActiveRecord object. For the examples that use rspec-rails, you should have a model in the file app/models/zombie.rb. This is what the spec in spec/models/zombie_spec.rb will look for. Also, your specs will need to require rails_helper rather than spec_helper.
# app/models/zombie.rb
class Zombie < ActiveRecord::Base
validates :name, presence: true
def hungry?
true
end
end
# spec/models/zombie_spec.rb
require 'rails_helper'
describe Zombie do
it 'is invalid without a name' do
zombie = Zombie.new
zombie.should_not be_valid
end
end
Zombie is extending ActiveRecord::Base but your code can't find ActiveRecord.
To fix that you can require 'activerecord' within zombie.rb. Depending on whether or not it's installed, you may need to also gem install activerecord from your command line or, alternatively, add gem 'activerecord' to your Gemfile and run bundle install
I found an example that worked well for me:
require 'rails_helper'
RSpec.describe Auction, :type => :model do
it "is valid with valid attributes"
it "is not valid without a title"
it "is not valid without a description"
it "is not valid without a start_date"
it "is not valid without a end_date"
end
I am new to ruby, so I am not 100% sure why it needs this ":type" attribute, but it fixed my problem.
(Source)
There are comments in the rails codebase that indicate that the test database should be reset between runs
rake -T
rake test:all # Run tests quickly by merging all types and not resetting db
rake test:all:db # Run tests quickly, but also reset db
config/database.yml
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
This doesn''t seem to be the case for me.
I'm using factory girl generate test models, here is an example factory
FactoryGirl.define do
factory :podcast do
sequence(:title) { |n| "Podcast #{n}" }
sequence(:feed_url) { |n| "http://podcast.com/#{n}" }
end
end
The podcast should have a unique feed_url so I validate it's uniqueness in the model.
class Podcast < ActiveRecord::Base
validates :feed_url, uniqueness: true, presence: true
end
In test_helper.rb I lint all factories
ENV["RAILS_ENV"] ||= "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'minitest/autorun'
FactoryGirl.lint
My test creates a podcast, builds another with the same name, then asserts that the second
is invalid.
require 'test_helper'
describe Podcast do
describe '#feed_url' do
it 'must be unique' do
podcast = create(:podcast)
new_podcast = build(:podcast, feed_url: podcast.name)
assert_invalid podcast, :feed_url, 'has already been taken'
end
end
end
The first time I run the tests it executes without errors and the tests all pass.
The second time I run the tests the Factory Girl lint fails because podcast feed_url has already been taken.
Why isn't the test database being rebuilt between runs?
We have a more involved FactoryGirl set up that prepares our database with some canonical items, but I think you could probably put this code directly in your test_helper.rb to assure the database is emptied:
# Destroy all models because they do not get destroyed automatically
(ActiveRecord::Base.connection.tables - %w{schema_migrations}).each do |table_name|
ActiveRecord::Base.connection.execute "TRUNCATE TABLE #{table_name};"
end
Alternatively, run rake db:test:prepare before every run.
There is a gem too that you can use, but I don't have any experience with it: http://rubygems.org/gems/database_cleaner.
The reason the database is not resetting is that you are running your tests outside of the database transaction that rails provides. The ActiveSupport::TestCase class is the basis for all rails tests. ActiveRecord adds a per-test database transaction to this class. This transaction will reset the database after each test. But you aren't running your tests with ActiveSupport::TestCase, you are running your tests with Minitest::Spec which isn't configured to run the transaction.
The simplest solution is to add minitest-rails to your Gemfile, and change the require in your test_helper.rb file from minitest/autorun to minitest/rails. If you would rather add your own support for Minitest's spec DSL you can use this article as a starting point.
Do you have another factory that might be creating a podcast via an association?
FactoryGirl linting builds each factory and checks it's validity, and if another factory has a podcast as an association, it'll create a podcast record.
FactoryGirl recommends clearing the database after running the linting. They use database_cleaner in their example:
https://github.com/thoughtbot/factory_girl/blob/2bf15e45305ac03315cf2ac153db523d3ce89ce1/GETTING_STARTED.md#linting-factories
If you're using 'Rspec' to be your unit test framework. After the installation of gem 'rspec-rails', you will got one configuration file called: spec/rails_helper.rb and within it you will find one configuration which looks like this:
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
this means that if it is true, then your each teat case will be running in a separate transaction.
I'm using devise in my Admin model like so:
class Admin < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Functionally, this is working fine. But I want to have specs for my Admin class, so I start to write a spec...
require 'spec_helper'
require 'admin'
describe Admin do
it 'mumblemumble' do ... end
end
...and then when I go to run the spec, it blows up:
active_record/dynamic_matchers.rb:22:in `method_missing': undefined method `devise' for Admin(Table doesn't exist):Class (NoMethodError)
How do I require Devise so that it's available in my spec? It seems like I should be able to
require 'devise'
in either my spec or my model (preferably my model), but this doesn't fix the issue or change my stack trace. How do I require Devise so that my model has the helper method available? I'm poking around the Devise gem but I'm not finding any clues.
How are you running these? RSpec directly? Or bundle exec rake spec?
There's this in your error: for Admin(Table doesn't exist) which makes me wonder if you have a database yet. The rake task should take care of setting up your world for you.
If that doesn't help, post your spec_helper.rb contents too.
Here's a basic Admin model I have:
class Admin < ActiveRecord::Base
devise :database_authenticatable, :recoverable, :rememberable,
:trackable, :validatable
end
And a basic spec:
require 'spec_helper'
describe Admin do
it { should validate_uniqueness_of(:email) }
end
Works great with vanilla generated rails app and generated devise setup.
This error
undefined method `devise' for Admin(Table doesn't exist):Class (NoMethodError)
seems to be that you don't have the table in the db? Did you migrate the rake file?
Yes it seems your test database does not have the admins table.
Try this:
bundle exec rake db:migrate db:test:prepare
db:migrate migrates your development database, if there are any pending migrations and db:test:prepare clones your test database according to the development one.
Got it. I run specs with an in-memory instance of sqlite3, so all the db:test:prepare doesn't apply to me. In addition to requiring Devise, it must also be setup/configured.
So in /spec/support/devise.rb:
require 'devise'
Devise.setup do |config|
require 'devise/orm/active_record'
end
And then in spec_helper.rb:
Dir["./spec/support/**/*.rb"].sort.each {|f| require f}