Timecop doesn't work with cucumber capybara - ruby-on-rails

Sorry for my english, it's not my native language
I have some time sensetive test. It's about some alert message when before event left less than 24h or more than 24h.
Some part of my code to find required events
left_less_than_24_hours_to_event = full_events_room.select{ |event| Time.now < Time.parse(event['event_time']) && Time.parse(event['event_time']) < (Time.now + 24.hours) }
left_more_than_24_hours_to_event = full_events_room.select{ |event| (Time.now + 24.hours) < Time.parse(event['event_time']) }
And have some cucumber scenario (fast translate to English (but it's dosen't matter about language))
Сценарий: User came in 2019-10-15 12:00
Допустим current date is "2019-10-15 12:00"
И go to root page
То see red message "Event Lesson-41 without room to Python-2 group"
И time return
I try binding.pry in my scenario and have time i really needed
In step definitions i have time which i needed
But in the same time i binding.pry my endpoint action in controller and have another time of my system!
Screenshot with time
Maybe i do something wrong? or how can i test something about time and output of browser like alert(bootstrap) messages in the same time?
This is what happen in step definitions
Допустим /^current date is "([^"]*)"/ do |date|
time_to_travel = Time.parse(date)
Timecop.travel(time_to_travel)
end
И /^time return/ do
Timecop.return
end

Have you tried travel_to helper in your specs?
You said you are using Timecop but have not attached any examples.
Here is a nice article from Andy Croll about moving from a Timecop to built in helpers

Related

Do I need to stub EVERYTHING in this method in order to pass?

I am generally new to mocha and I hate using this gem but I need to use it in order to pass a test that I am constructing. What is giving me problems is what I'm supposed to mock and how I am supposed to mock it. To illustrate my point, Here is an example of a method that I am testing:
def statistics_of_last_24_hrs
stats = ses.statistics.find_all { |s| s[:sent].between?(Time.now.utc - 24.hours, Time.now.utc) }
sent_last_24_hrs = ses.quotas[:sent_last_24_hours].to_f
no_of_bounces = stats.inject(0.0) { |a, e| a + e[:bounces] }
no_of_complaints = stats.inject(0.0) { |a, e| a + e[:complaints] }
bounce_rate = sent_last_24_hrs.zero? ? 0.0 : (no_of_bounces / sent_last_24_hrs) * 100
complaint_rate = sent_last_24_hrs.zero? ? 0.0 : (no_of_complaints / sent_last_24_hrs) * 100
fail(Reverification::SimpleEmailServiceLimitError, 'Bounce Rate exceeded 5%') if bounce_rate >= 5.0
fail(Reverification::SimpleEmailServiceLimitError, 'Complant Rate exceeded .1%')if complaint_rate >= 0.1
end
Basically what this code is doing is getting some statistics from an Amazon api call and then calculating them to determine if my bounce/complaint rate has exceeded the limit. The limit is 5% and 0.1% respectively.
Basically for my test all I really need to do is to stub the variables bounce_rate and complaint_rate in order to test whether the right exception is thrown.
This is where I am getting stuck. Here is a barebones test that I would ideally write:
it 'should raise SimpleEmailServieLimitError if bounce rate is above 5%' do
assert_raise Reverification::SimpleEmailServiceLimitError do
Reverification::Process.statistics_of_last_24_hrs
end
end
How can I stub the bounce_rate and then the complaint_rate. I've done some searching around and came to the conclusion that there isn't a way to stub variables. I also looked at this link List of Mocha MethodsWhich confirms my findings.
Is there a way I can just write a test like this:
it 'should raise SimpleEmailServieLimitError if bounce rate is above 5%' do
stubs(:bounce_rate).returns(true)
assert_raise Reverification::SimpleEmailServiceLimitError do
Reverification::Process.statistics_of_last_24_hrs
end
end
Or do I have to stub every method call in this method so that the test will look something like this:
it 'should raise SimpleEmailServieLimitError if bounce rate is above 5%' do
sent_last_24_hrs = 20
over_bounce_limit = MOCK::AWS::SimpleEmailService.over_bounce_limit
AWS::SimpleEmailService.any_instance.stubs(:statistics).returns(stub(find_all: over_bounce_limit))
AWS::SimpleEmailService.any_instance.stubs(:quotas).returns(stub(sent_last_24_hours: sent_last_24_hrs))
etc. etc. etc...........
assert_raise Reverification::SimpleEmailServiceLimitError do
Reverification::Process.statistics_of_last_24_hrs
end
end
Is there an easier way to do this?
Even if there was a way to stub local variables, that feature would produce tests that are very hard to maintain because you'd not be able to refactor your code without changing tests.
Nested stubs are design smell too - your tests will know about too much implementation details and will become unmaintainable.
The same can be said for stubbing third-party code as any changes to the third-party library will allow your tests to pass while the code does not work.
It is a lot better to create your own wrapper around AWS SimpleEmailService - gateway. You'd implement it to have a very narrow stable interface like
class BounceStatistics
def no_of_bounces
def no_of_complaints
def sent_last_24_hrs
end
Since this interface is your own and it is stable, you can safely stub it and provide alternative implementation for your tests:
assert_raise Reverification::SimpleEmailServiceLimitError do
Reverification::Process.statistics_of_last_24_hrs(
stub(no_of_bounces: 2, no_of_complaints: 3, sent_last_24_hrs: 5))
end
alternatively you may implement it as
BounceStatistics.any_instance.stubs(:no_of_bounces).returns(2)
BounceStatistics.any_instance.stubs(:no_of_complaints).returns(3)
BounceStatistics.any_instance.stubs(:sent_last_24_hrs).returns(5)
assert_raise Reverification::SimpleEmailServiceLimitError do
Reverification::Process.statistics_of_last_24_hrs
end
However passing dependencies explicitly allows you to have more maintainable code and simpler tests.

Ice cube, how to set a rule of every day at a certain time for Sidetiq/Fist of Fury

Per docs I thought it would be (for everyday at 3pm)
daily.hour_of_day(15)
What I'm getting is a random mess. First, it's executing whenever I push to Heroku regardless of time, and then beyond that, seemingly randomly. So the latest push to Heroku was 1:30pm. It executed: twice at 1:30pm, once at 2pm, once at 4pm, once at 5pm.
Thoughts on what's wrong?
Full code (note this is for the Fist of Fury gem, but FoF is heavily influenced by Sidetiq so help from Sidetiq users would be great as well).
class Outstanding
include SuckerPunch::Job
include FistOfFury::Recurrent
recurs { daily.hour_of_day(15) }
def perform
ActiveRecord::Base.connection_pool.with_connection do
# Auto email lenders every other day if they have outstanding requests
lender_array = Array.new
Inventory.where(id: (Borrow.where(status1:1).all.pluck("inventory_id"))).each { |i| lender_array << i.signup.id }
lender_array.uniq!
lender_array.each { |l| InventoryMailer.outstanding_request(l).deliver }
end
end
end
Maybe you should use:
recurrence { daily.hour_of_day(15) }
instead of recurs?

RSpec - expected equal?( Time Object ) to return true, got false

I'm trying (and failing!) the following code to compare two Time objects in RSpec:
describe '#current_shift_datetime' do
subject { WipStack.current_shift_datetime }
it "To be a Time object" do
expect(subject).to be_kind_of(Time)
end
it "To return current shift datetime" do
# Trying to figure out why they are not equal
puts subject.in_time_zone.round
puts 0.day.ago.midnight.in_time_zone.round
# Problematic code here -->
expect(subject.in_time_zone.round).to be_equal(0.day.ago.midnight.in_time_zone.round)
# End of problematic code
end
end
I have read a couple of things in the internet about rspec time comparisong, one of them explaining the problem with the milliseconds (therefore the round) and another page talking about stub but then i ended with An error occurred in an after hook SystemStackError: stack level too deep.
The ouput of the test is:
1) WipStack#current_shift_datetime To return current shift datetime
Failure/Error: expect(subject.in_time_zone.round).to be_equal(0.day.ago.midnight.in_time_zone.round)
expected equal?(Mon, 17 Feb 2014 00:00:00 CST -06:00) to return true, got false
The puts output:
#current_shift_datetime
To be a Time object
2014-02-17 00:00:00 -0600
2014-02-17 00:00:00 -0600
To return current shift datetime (FAILED - 1)
Update:
Here is the current_shift_datetime method:
def WipStack.current_shift_datetime(scope = nil)
shifts = WipStack.get_shifts(scope)
current_shift = (Time.zone.now - 1.day).strftime("%m/%d/%Y")+" "+shifts.last
current_time = Time.zone.now.strftime("%H:%M")
shifts.each do |shift|
if current_time > shift
current_shift = Time.zone.now.strftime("%m/%d/%Y")+" "+shift
end
end
Time.zone.parse(current_shift)
end
Rather than compare the time zones directly (and get bogged down in subtle differences), you could try comparing the string representations to each other, since that's what you're checking with in the console anyhow.
Depending on how you use this timezone in your app, this should be sufficient to say they are essentially the same time.
expect(subject.in_time_zone.round.to_s).to eq(0.day.ago.midnight.in_time_zone.round.to_s)
You could probably drop the .round if you wanted to as well.
EDIT
Try changing your code so there is no difference between what is printed and what is compared:
it "To return current shift datetime" do
a = subject.in_time_zone.round.to_s
b = 0.day.ago.midnight.in_time_zone.round.to_s
puts a
puts b
expect(a).to eq(b)
end
I fixed it thanks to tyler! It seems that be_equal was actually trying to see if both objects were the same (a comment appear on the test failing when comparing the strings), changing be_equal to eq made the test pass!
it "To return current shift datetime" do
#pending
puts subject.in_time_zone.round.to_s
puts 0.day.ago.midnight.in_time_zone.round.to_s
expect(subject.in_time_zone.round.to_s).to eq(0.day.ago.midnight.in_time_zone.round.to_s)
end

Getting system uptime in ruby

I am looking for a better way to retrieve a systems uptime. My current method works, but I feel like things can be done better.
def uptime_range
days_up = `uptime | awk {'print$3'}`.chomp.to_i
hours_up = `uptime | awk {'print$5'}`.delete(',').chomp
seconds_up = time_to_seconds(days_up,hours_up)
started = Time.now - seconds_up
"#{started.strftime('%Y.%m.%d.%H.%M')}-#{Time.now.strftime('%Y.%m.%d.%H.%M')}"
end
On linux systems you can open /proc/uptime and read seconds since the machine booted.
def started
Time.now - IO.read('/proc/uptime').split[0].to_f
end
Edit: changed to_i to to_f. With to_i, the value of started when displayed as a string or integer is more likely to vary, especially if the boot time was close to the middle of a second rather than at the beginning or end of a second.
You could check the sysinfo gem. It should give system-independent access to the uptime data.
A rubygem called linux_stat can do that pretty well:
require 'linux_stat'
LinuxStat::OS.uptime
=> {:hour=>40, :minute=>46, :second=>19.75}
Code copied from here.
But if you don't feel like using another gem for this trivial task, you can do this:
uptime = IO.read('/proc/uptime').to_f
uptime_i = uptime.to_i
puts({
hour: uptime_i / 3600,
minute: uptime_i % 3600 / 60,
second: uptime.%(3600).%(60).round(2)
})
Which prints:
{:hour=>0, :minute=>39, :second=>54.89}

What are RSPEC Stubs? By example

I just can't seem to get a hold of what exactly stubs are.
Could someone just explain what the following RSPEC code is supposed to do. And what is the benefit of using stub here?
require "performance_monitor"
require "time" # loads up the Time.parse method -- do NOT create time.rb!
describe "Performance Monitor" do
before do
#eleven_am = Time.parse("2011-1-2 11:00:00")
end
it "takes exactly 1 second to run a block that sleeps for 1 second (with stubs)" do
fake_time = #eleven_am
Time.stub(:now) { fake_time }
elapsed_time = measure do
fake_time += 60 # adds one minute to fake_time
end
elapsed_time.should == 60
end
end
I think I'll be able to understand with an example.
stub is used here for override the function now of Time so here instead of return current time which you got from Time.now after stub it will return fake_time
Note that stub will only 'override' this method only in this one spec. Other specs will respond do Time.now properly

Resources