I'm tryna improve my RSpec skills following the bests practices.
I have a simple method is_multiple_of_3_or_5? and I'm implementing a test for it using simple testing values. My first approach was:
describe '#is_multiple_of_3_or_5?' do
context "when input is multiple of 3 or 5" do
expect(is_multiple_of_3_or_5?(3)).to be true
expect(is_multiple_of_3_or_5?(5)).to be true
expect(is_multiple_of_3_or_5?(51)).to be true
expect(is_multiple_of_3_or_5?(45)).to be true
end
end
I like it because it's very short and the test cases are simple, so no need to complicate it.. But I've read in the RSpec style guide that it should be only one expectation per example.
So I refactored in 2 other ways:
One it per example:
describe '#is_multiple_of_3_or_5?' do
context 'when input is multiple of 3 or 5' do
it "is true for 3" do
expect(is_multiple_of_3_or_5?(3)).to be true
end
it "is true for 5" do
expect(is_multiple_of_3_or_5?(5)).to be true
end
it "is true for 51" do
expect(is_multiple_of_3_or_5?(51)).to be true
end
it "is true for 45" do
expect(is_multiple_of_3_or_5?(45)).to be true
end
end
end
Drying up with a subject block
describe '#is_multiple_of_3_or_5?' do
context 'when input is multiple of 3 or 5' do
subject { is_multiple_of_3_or_5?(input) }
context 'when input is 3' do
let(:input) { 3 }
it { is_expected.to be true }
end
context 'when input is 5' do
let(:input) { 5 }
it { is_expected.to be true }
end
context 'when input is 51' do
let(:input) { 51 }
it { is_expected.to be true }
end
context 'when input is 45' do
let(:input) { 45 }
it { is_expected.to be true }
end
end
end
So I have 2 questions:
In the case of theses 2 last versions, the test became really bigger, is it really worth it? Considering that the values tested are simple integers?
Which one of these 2 versions are better formatted?
Thank you for anyone who could help me take better perspective on RSpec!
Related
Thank you for your time
I have a large animation I am working on with several if statements with many outputs
I join the outputs with & ampersand and it all works ok, but when I try to call the visibility of a movie clip, it will not work. Here is a simple one I wrote using three movie clips.
this.black_sw.addEventListener("click", fl_ClickToHide.bind(this));
function fl_ClickToHide()
{ if(this.black_mc. visible== true)(
this.black_mc.visible = false
this.redball.visible = true
)
else if(this.black_mc. visible== false)
(this.black_mc.visible = true
this.redball.visible = false)
}
This does not work console error if I add a "&" to join the statements also console error, deleting
one of the (visible = ) conditions in each part, it works, why, I am stumped.
best regards peter
this.black_sw.addEventListener("click", fl_ClickToHide.bind(this));
function fl_ClickToHide()
{
if (this.black_mc.visible)
{
this.black_mc.visible = false
this.redball.visible = true
}
else
{
this.black_mc.visible = true
this.redball.visible = false
}
}
This should work
I follow the book railstutorial of micheal hartl. at chapter 11 my test fails in users_signup_test.rb. in my account_activations_contrller.rb file, the action edit, user.authenticated?(:activation, params[:id]) is always false
I had the same problem which was me forgetting to refactor the authenticated? method properly
I had:
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if remember_digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
Notice the remember_digest.nil?
It should be:
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
debugger
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
Hope this helps 😊
Sorbet infers the type of true to be TrueClass, and the type of false to be FalseClass. Often it would be nice if it would instead infer T::Boolean. Why not special case true and false to have the type T::Boolean instead?
It's possible to work around this problem with a type annotation, initializing variables with T.let(true, T::Boolean) for example, but it would be nice to not have to provide this extra information.
# typed: true
T.reveal_type(true) # Revealed type: `TrueClass`
T.reveal_type(false) # Revealed type: `FalseClass`
extend T::Sig
sig {params(x: T::Boolean).void}
def test(x)
var = true
10.times do
var = false # Changing the type of a variable in a loop is not permitted
end
end
The assignment of false to var in the loop causes an error to be raised, as the type of var is being changed from TrueClass to FalseClass.
Sorbet's flow-sensitive typing is made more precise by true and false having different types. In the following example, a variable with the value true is used as the condition of an if-statement:
# typed: true
val = true
if val
puts "true!"
else
puts "false?"
end
The resulting error from sorbet is:
editor.rb:7: This code is unreachable https://srb.help/7006
7 | puts "false?"
^^^^^^^^
Errors: 1
Behind the scenes, sorbet knows that the value being examined has the type TrueClass, and that the value true is the only value of that type. As a result, it knows that val cannot be false, and that the else branch will never be executed.
Now consider the case where we instead infer the type T::Boolean for true and false. T::Boolean is a synonym for T.any(TrueClass, FalseClass), so in the example it now means that val could be either true or false. As a result, it becomes impossible to tell from the type alone that the else branch will not be executed.
The flow-sensitive typing documentation on sorbet.org has more information on this topic.
Asked the same question today as well.
Ended up fixing it as you suggested with type annotations and initializing with T.let(true, T::Boolean)
# typed: true
extend T::Sig
sig {params(x: T::Boolean).void}
def test(x)
var = T.let(true, T::Boolean)
10.times do
var = T.let(false, T::Boolean)
end
end
i have been learning to using rspec.
i have to test this one:
Book.where(old:false, id:user.collections.select(:book_id).group(:book_id).map(&:book_id)).map{|b|[b.name, b.id] }
what i've already done is :
#user = Fabricate(:user)
#book1 = Fabricate(:book, old:false)
#book2 = Fabricate(:book, old:true)
#collection = Fabricate(:collection, book_id:#book1.id)
#collection2 = Fabricate(:collection, book_id:#book2.id)
#user.collections << #book1 << #book2
#books = Book.where(old:false, id:#user.collections.select(:book_id).group(:book_id).map(&:book_id)).map{|b|[b.name, b.id]}
.....
it "books" do
subject[:filter].should == #books.map{|d| [d.name, d.id]}
end
it expected a book, but i got 0. did i forget something?
thanks in advance for any idea and help!
Your code is very unreadable and hard to test. You should split the execution into smaller methods.
Book.where(old:false, id:user.collections.select(:book_id).group(:book_id).map(&:book_id)).map{|b|[b.name, b.id] }
could be
class Book
def self.find_books_with_name(ids)
where(old: false, id: ids).map { |b| [b.name, b.id] }
end
end
Then you can call it with:
Book.find_books_and_name(user.collections.select(:book_id).group(:book_id).map(&:book_id))
which appears equally insane to me. Add to your user model and collection model something like
class Collection
def self.book_ids
select(:book_id).group(:book_id).map(&:book_id)
end
end
Now you have
Book.find_books_and_name(user.collections.book_ids)
and now you can (and you should) test each method separately. A failure will be easier to spot and the test itself will be much more simple, without all that coupling to various models and records.
I'm using the Twitter gem, and I want to take a list of users from my Friend model and test to see if they're being following a Twitter user, then ... unfollow if the Twitter API returns false. Trouble is, it seems the code I'm using below never evaluates as false, even when the API returns false so it's always reverting to the else section.
I've tried variations like if ret == 'false' and it's not worked.
ret = #t.friendship?(#id_str, f.screen_name)
if ret == false
#t.unfollow(f.screen_name)
puts "#{f.screen_name} has been unfollowed."
self.update_unfollowed_column(f.id, false)
else
self.update_following_column(f.id)
end
In the console, though, if I do:
r = t.friendship?('user1', 'user2')
=> false
And then, I can do:
r == false
=> true
So, I'm not sure exactly what I'm doing wrong with the code in my Friend model.
Before the if statement, try debugging what the result is from the friendship? method:
logger.info ret.class.name # => FalseClass if it's actually false,
# => String if it's 'false'
logger.info ret.inspect # => "false" if it's false.
logger.info((ret == false).inspect) # => Should be "true"