Run Rspec test block conditionally based on gem config - ruby-on-rails

I'm trying to add write a rspec test that is dependent on what the gem user has set as a configuration. So i want to run the test with a certain configuration.
This is the configuration:
Tasuku.configure do |config|
config.update_answers = false
end
And this is the test that of course is only sensible when the configuration above is set to false:
describe '#can_only_answer_each_question_once' do
let!(:question) { create :question_with_options }
let!(:answer) { create :question_answer, author: user, options: [question.options.first] }
let!(:duplicate_answer) { build :question_answer, author: user, options: [question.options.first] }
it 'prohibits an author from answering the same question more than once' do
expect(duplicate_answer).not_to be_valid
end
it 'should have errors' do
expect(duplicate_answer.errors_on(:base)).to eq [I18n.t('tasuku.taskables.questions.answers.already_answered')]
end
end

Try using RSpec's filters. More info here: https://www.relishapp.com/rspec/rspec-core/v/2-8/docs/filtering/if-and-unless
For example:
describe '#can_only_answer_each_question_once', unless: answers_updated? do

The solution i ended ut using was setting the setting right before the it blocks in the correct context/describe block.
One example was is this:
describe '#can_only_vote_once_for_single_choice_questions' do
before(:all) do
::Tasuku.configure do |config|
config.update_answers = false
end
end
let!(:question) { create :question_with_options, multiple: false }
let!(:answer) { build :question_answer, author: user, options: [question.options.first, question.options.second] }
it 'prohibits an author from answering the same question more than once' do
expect(answer).not_to be_valid
end
it 'should have errors' do
expect(answer.errors_on(:base)).to eq [I18n.t('tasuku.taskables.questions.answers.can_only_vote_once')]
end
end

Related

How to test an if-condition not been excuted?

I'm new to Rspec. I have a code like this:
if #context.persona == :system && #context.scopes&.include?(SEARCH_SCOPE)
return <something>
end
I want to write a unit test to confirm the #context.scopes&.include?(SEARCH_SCOPE) is not being executed when #context.persona is not :system. Here is what I wrote:
context 'when persona is system' do
let(:persona) { :system }
it 'checks the scope' do
allow(context).to receive(:scopes)
expect(context).to have_received(:scopes)
end
end
context 'when persona is not system' do
let(:persona) { :user }
it 'checks the scope' do
allow(context).to receive(:scopes)
expect(context).not_to have_received(:scopes)
end
end
The second test passed, but the first test failed with:
Failure/Error: expect(context).to have_received(:scopes)
(Double (anonymous)).scopes(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
Could someone help me? I googled it before but didn't see anything helpful. I'm sorry if it is duplicated.
Not a direct answer to your question, but you are falling into the pit of testing implementation, instead of behaviour. Don't do that.
Your test shouldn't care about this:
expect(context).not_to have_received(:scopes)
Instead, your test should only be doing something like this:
context 'when persona is system and scopes includes SEARCH_SCOPE' do
let(:persona) { :system }
let(:scopes) { ... }
it 'returns <something>' do
expect(the_method_being_invoked).to eq(<something>)
end
end
context 'when persona is not system' do
let(:persona) { :user }
let(:scopes) { ... }
it 'returns <something-else>' do
expect(the_method_being_invoked).to eq(<something-else>)
end
end
context 'when scopes is empty' do
let(:persona) { :user }
let(:scopes) { nil }
it 'returns <something-else>' do
expect(the_method_being_invoked).to eq(<something-else>)
end
end
Why? Because when you refactor code, and the implementation changes, you don't want specs to start failing unless the behaviour has also changed.
You should usually even be able to write the test before writing the method -- therefore having no knowledge of its implementation details.

Getting Rspec unit test coverage with Rails and PostgreSQL

I am trying to write a unit test for the following model concern...
require 'active_support/concern'
module Streamable
extend ActiveSupport::Concern
def stream_query_rows(sql_query, options = 'WITH CSV HEADER')
conn = ActiveRecord::Base.connection.raw_connection
conn.copy_data("COPY (#{sql_query}) TO STDOUT #{options};") do
binding.pry
while row = conn.get_copy_data
binding.pry
yield row
end
end
end
end
So far I have battling this with the following spec...
context 'streamable' do
it 'is present' do
expect(described_class.respond_to?(:stream_query_rows)).to eq(true)
end
context '#stream_query_rows', focus: true do
let(:sql_query) { 'TESTQRY' }
let(:sql_query_options) { 'WITH CSV HEADER' }
let(:raw_connection) do
Class.new do
def self.copy_data(args)
yield
end
def self.get_copy_data
return Proc.new { puts 'TEST' }
end
end
end
before do
allow(ActiveRecord::Base).to receive_message_chain(:connection, :raw_connection).and_return(raw_connection)
described_class.stream_query_rows(sql_query)
end
it 'streams data from the db' do
expect(raw_connection).to receive(:copy_data).with("COPY (#{sql_query}) TO STDOUT #{sql_query_options};")
end
end
end
While I can get the first expect to pass, meaning, I can trigger the first binding.pry, no matter what I try, I can not seem to get past the second.
This is the error...
LocalJumpError:
no block given (yield)
I am only trying to unit test this and ideally not hit the db, only testing the communication of the objects. This also, can and will be used in many models as an option for streaming data.
Reference article: https://shift.infinite.red/fast-csv-report-generation-with-postgres-in-rails-d444d9b915ab
Does anyone have an pointers on how to finish this stub and or adjust the spec so I have the following block covered?
while row = conn.get_copy_data
binding.pry
yield row
end
ANSWER
After reviewing the comments and suggestions below, I was able to refactor the spec and now have 100% coverage.
context '#stream_query_rows' do
let(:sql_query) { 'TESTQRY' }
let(:sql_query_options) { 'WITH CSV HEADER' }
let(:raw_connection) { double('RawConnection') }
let(:stream_query_rows) do
described_class.stream_query_rows(sql_query) do
puts sql_query
break
end
end
before do
allow(raw_connection).to receive(:copy_data).with("COPY (#{sql_query}) TO STDOUT #{sql_query_options};"){ |&block| block.call }
allow(raw_connection).to receive(:get_copy_data).and_return(sql_query)
allow(ActiveRecord::Base).to receive_message_chain(:connection, :raw_connection).and_return(raw_connection)
end
it 'streams data from the db' do
expect(raw_connection).to receive(:copy_data).with("COPY (#{sql_query}) TO STDOUT #{sql_query_options};")
stream_query_rows
end
it 'yields correct data' do
expect { stream_query_rows }.to output("#{sql_query}\n").to_stdout_from_any_process
end
end
Like the error says, you're yielding, but you haven't supplied a block for it to call.
If your method expects a block, then you need to supply one when you call it.
To do that, you need to change this line:
described_class.stream_query_rows(sql_query)
to something like this:
described_class.stream_query_rows(sql_query) { puts "this is a block" }

What a test should test with Rspec in Ruby on Rails application

I'm beginner in Rspec, and actually we asked me to do Rspec test for somes method which are already build but which never have been test (they don't write test before building the method).
And now I'm struggling to know how can I test my method, here is example:
class ConnectorJob < ActiveJob::Base
queue_as :connector
def perform(tenant_id = nil, debug = false)
tenant_to_sync = Tenant.to_sync(tenant_id)
return if tenant_to_sync.empty?
tenant_to_sync.each do |tenant|
service = MyAPP::ContactToSync.new(tenant, debug).call
if service.success?
ConnectorService::Synchronization.new(
tenant, service.data, debug
).call
end
end
end
end
What should I test on this? Should I test the return value is correct or if other method are well called?
Here is what I tried to do
require "rails_helper"
RSpec.describe ConnectorJob, type: :job do
it 'is in connector queue' do
expect(ConnectorJob.new.queue_name).to eq('connector')
end
describe 'perform' do
let (:tenant) { create(:tenant) }
let (:job) { ConnectorJob.new.perform(tenant.id) }
context 'with empty tenant' do
it { expect(ConnectorJob.new.perform(#tenant.id)).to eq nil }
end
context 'with tenant' do
it { expect(ConnectorJob.new.perform(tenant.id)).to eq job }
end
end
end
As you can see my last test doesn't have sense but I have no idea what I should write on my Rspec for anticipate the result of this method.
If I check my Rspec coverage, Rspec is telling me I cover 100% of my method but I'm not sure that is correct.
I hope I'm clear, feel free to ask me more details.
Thank you all
I think you should test final result, I mean result after calling
ConnectorService::Synchronization.new(...).call and test three cases, e.g. if this call create new user, you should test it:
If tenant_to_sync.empty? == true
context 'with empty tenant' do
it { expect(ConnectorJob.new.perform(#tenant.id)).to change(User.count).by(0) }
end
If service.success? == false
context 'MyAPP::ContactToSync return false' do
it { expect(ConnectorJob.new.perform(#tenant.id)).to change(User.count).by(0) }
end
If service.success? == true
context 'success' do
it { expect(ConnectorJob.new.perform(#tenant.id)).to change(User.count).by(1) }
end
It should be enough to cover all scenarios.

RSpec "count" change.by?

So I was looking at: https://rubyplus.com/articles/1491-Basic-TDD-in-Rails-Writing-Validation-Tests-for-the-Model
Just seeing techniques of testing and I saw this:
require 'rails_helper'
describe Article, type: :model do
it 'is valid if title and description fields have value' do
expect do
article = Article.new(title: 'test', description: 'test')
article.save
end.to change{Article.count}.by(1)
end
end
Specifically the last line: end.to change{Article.count}.by(1). From reading https://relishapp.com/rspec/rspec-expectations/v/3-7/docs/built-in-matchers/change-matcher
It says specifically:
The change matcher is used to specify that a block of code changes
some mutable state. You can specify what will change using either of
two forms:
Which makes sense. But were testing Article.count in the block of code which isn't actually "doing" anything (The article.save is what actually changed the Article.count so how exactly does this work? Does the test take a a look at whats in the block of code before it's ran and "prerun" it...the compare the .by(1) after?
Thanks
There are two blocks of code being executed. The block of code passed to expect, and the block of code passed to change. This is what's really happening, in pseudo-code.
difference = 1
initial_count = Article.count
article = Article.new(title: 'test', description: 'test')
article.save
final_count = Article.count
expect(final_count - initial_count).to eq(difference)
I would refactor your test to be a little easier to follow as this:
require 'rails_helper'
describe Article, type: :model do
let(:create_article) { Article.create(title: 'test', description: 'test') }
it 'is valid if title and description fields have value' do
expect { create_article }.to change { Article.count }.by(1)
end
end

RSpec doesn't show full difference between return and expected

When a run a test with JSON, the rspec doesn't show the full spec, so I can't see the diference between return and expected.
The message of diff is shortened with ...
expected: "{\"id\":1,\"number\":1,\"sequential\":1,\"emitted_at\":\"2014-01-01T13:35:21.000Z\",\"status\":\"aut...erenceds_attributes\":[{\"id\":null,\"nfe_key\":\"42150707697707000148550010000020101000020105\"}]}"
got: "{\"id\":1,\"number\":1,\"sequential\":1,\"emitted_at\":\"2014-01-01T13:35:21.000Z\",\"status\":\"aut...erenceds_attributes\":[{\"id\":null,\"nfe_key\":\"42150707697707000148550010000020101000020105\"}]}"
aut...erenceds_attributes look in middle of message
My script test:
RSpec.describe InvoiceSerializer do
let(:invoice) do
build :invoice, :testing_serializer
end
subject { described_class.new invoice }
it "returns a json" do
expected = {
id: 1,
number: 1,
sequential: 1,
emitted_at: "2014-01-01T13:35:21.000Z",
status: "authorized",
invoice_bills_attributes: [{
id: nil,
expire_at: "2014-01-02T00:00:00.000Z",
value: "1.23"
}],
...
}.to_json
expect(subject.to_json).to eq expected
end
end
Example of error in my console
What gem/plugin or expectation that you use to check your test?
I use the console and Rubymine IDE.
Now I use:
puts "1 --> #{subject.to_json}"
puts "2 --> #{expected}"
And I don't like to write this for to debbug my test.
Set RSpec::Support::ObjectFormatter.default_instance.max_formatted_output_length to a high value
Update: as Yurri suggested, it might be better to better to set it to nil
This might help: https://github.com/waterlink/rspec-json_expectations
As a bonus, it allows you to specify your tests in terms of a subset of attributes, which can be used to create more granular tests.
To build on previous answers, and utilize the RSpec.configure syntax you'll want to use something like this:
RSpec.configure do |rspec|
rspec.expect_with :rspec do |c|
# Or a very large value, if you do want to truncate at some point
c.max_formatted_output_length = nil
end
end

Resources