I have been struggling with a very weird Capybara/Rspec behavior that has something to do with Time.now mocking.
First, I tried to define a set of specs with some models having three date fields set to the value on DateTime.stub(:now) { DateTime.now.utc }. When executed the whole test suite by doing rspec spec the test always failed, however, when doing rspec spec/features/feature all defined specs succedded.
While researching, some people suggested to use Timecop or Delorean gems to fake the current time to avoid having problems with Capybara. I tried both with no luck. I added to the before/after each blocks something like Timecop.travel(time); all suite's specs succeeded this time, but executing the single spec file did yield failing specs .
What would be the best way to garantee deterministic outcomes from specs that deal with time as in Compute my bicycle average speed during the last month/week/day ?
I want to ALWAYS execute all such specs (defined in rspec spec/features/feature) within a given fixed date, let's say June 13, 2010 15:70:00 so that I can do, DateTime.now-1.year and I get June 13, 2009 15:70:00, and the result is always consistent regardless if I am executing it as a separate spec file or as a suite.
Example of failing test:
describe "Stats Spec" do
include IntegrationHelper
before :each do
I18n.locale = :en
DateTime.stub(:now) { DateTime.new(2012,10,11,14,50).utc }
end
context "Given Loui has been cycling during the last year" do
before(:each) do
#loui = Fabricate(:standard_user, username: 'Loui')
stub_track(:today, #loui.id, 3000)
stub_track(:this_week, #loui.id, 5000)
stub_track(:this_month, #loui.id, 8000)
stub_track(:six_months_ago, #loui.id, 3000)
end
specify "he should be able to see his kms and today stats", js: true do
# This spec checks that 3 km appear when clicked on "show today stats" button
end
specify "he should be able to see his kms and week stats", js: true do
# This spec checks that 3+5 km appear when clicked on "show weekly stats" button
end
end
end
def stub_track(time, user_id, meters)
# Fabricates a track using DateTime.now-(3.days/1.week/2.months) with the provided
# user_id and provided meters
end
Related
I have a test that I want to run only when I specifically indicate that I want to run it.
Say I have a test that does a whack load.. It's a test that takes a user through all the steps necessary to purchase something from when the user signs up to when they bid on something and then when they pay for it, and even when they later get a refund for the purchase.
Now typically a massive test is a really bad idea. Tests should be small and discrete. I'm all for that. And I have those tests in place. But I also think that one omni-test is a good idea. Especially for something as significant as payments.
I expect the test to be slow and all over the database... so I don't really want to run it all that often. (only when I specifically ask for it)
So I'm wondering if there is a way to have a test only run when I specifically ask for. If I type "rspec' in my terminal then this massive test won't run.
Sounds like you are looking to isolate certain 'features' to be run. Cucumber and Capybara use rspec underneath the covers to just this. Using this structure will be skipped unless specifically called. From the rspec core, use the tag options.
Example from https://www.relishapp.com/rspec/rspec-core/v/2-4/docs/command-line/tag-option
describe "group with tagged specs" do
it "example I'm working now", :focus => true do; end
it "special example", :type => 'special' do; end
it "slow example", :skip => true do; end
it "ordinary example", :speed => 'slow' do; end
it "untagged example" do; end
end
Then, to test:
When I run "rspec . --tag mytag"
Features Specs
Furthermore, using cucumber allows for feature tags for view testing.
You can use ENV variables to conditionally change your configuration. For example, I use the following in my spec_helper.rb for disabling feature tests by default:
config.filter_run_excluding :type => 'feature' unless ENV['FEATURE']
Then, if you want to run the full test suite from your console, simply:
FEATURE=true rspec
You should be able to tweak this to suit your needs. Just remember to set the environment variable on your CI server if you want the feature specs to be run there.
I'm trying to mock Date.today.wday in a rake task in rspec.
Gem versions: RSpec 2.14.8 --- Rails 4.1.1 --- ruby 2.0.0
Here is a simplified fake version of my test to illustrate essentially what I'm trying to do:
describe "scheduler" do
describe ":thursday_invitations" do
let(:run_issue_invites) do
Rake::Task[:thursday_invitations].reenable
Rake.application.invoke_task :thursday_invitations
end
before do
Rake.application.rake_require 'tasks/scheduler'
Rake::Task.define_task(:environment)
Date.today.should_receive(:wday).and_return(4) ###MY NEMESIS CODE LINE
end
context "on thursday" do
it "issues invitations" do
expect(Date.today.wday).to eq(4) ###THE VERIFICATION TEST THAT KEEPS FAILING
run_issue_invites
expect(<other_stuff_to_test>).to <return_properly>
end
end
end
end
So, the real key of this is mocking out the Date.today.wday. Because I want to be able to run my spec on any day of the week, I need to mock/stub out this method to always return "4" (the day-number for Thursday in Rails). So, I initially setup my test to first verify that it is receiving a "4" (the assertion in the test). If today is, say, Friday (which it is) and I run the test, it fails saying that it expected "4" but got "5". That is, it is not returning the value that I want it to when I receive the method. I have tried stubbing with similar ineffective results. Normally, mocking is a breeze, but what seems to be the hangup is .wday which operates on Date.today.
Because this is a rake task (which I'm not as familiar with mocking), I may have to specify something further, but I haven't been able to get to the bottom of it...
Let me know if you need any other clarifying information.
I believe the reason you're not seeing the behavior you expect is that the object you are mocking is the not the same object under test.
In a Rails 4+ environment, this is what I see on the rails console:
[1]> Date.today.object_id
70104549170200
[2]> Date.today.object_id
70104552970360
The fact that the object_id is different in subsequent calls to Date.today means that each call returns a new object. So Date.today.should_receive(:wday).and_return(4) is setting an expectation on an object that will never be used again.
You'll need to rewrite your spec to ensure the same object is returned by Date.today each time. Here's one solution, omitting other parts of your example for clarity:
let!(:today) { Date.today }
before do
Date.stub(:today).and_return(today)
today.should_receive(:wday).and_return(4)
end
it "issues invitations" do
expect(Date.today.wday).to eq(4)
end
I have a page that creates a snapshot of a document. That document is saved with the title being a timestamp (September 27, 2014 at 4:01:10 pm) for example. I am writing a test for this page and want to stub time so that it doesn't change.
What I have at the moment is Time.stubs(:now).returns(Time.parse("2014-1-2 11:00:00")) but when I do that I get an error message saying:
Capybara::FrozenInTime: time appears to be frozen, Capybara does not work with libraries which freeze time, consider using time travelling instead
What is the best way to stub out time here?
Rails now includes support for time travel directly, e.g.:
feature 'Time travel verifier' do
include ActiveSupport::Testing::TimeHelpers
scenario 'works in the past' do
travel_to(1.day.ago) do
visit time_travel_verification_path
expect(page).to have_content('WOAH Time Travel!')
end
end
end
I'm reposting here my comment as an answer.
There is the timecop gem https://github.com/travisjeffery/timecop
It allows you to do things like the following:
describe "some set of tests to mock" do
before do
Timecop.freeze(Time.local(1990))
end
after do
Timecop.return
end
it "should do blah blah blah" {}
end
which will make the tests run as if it was 1990-1-1 and then return back to the current time.
My Signup database has an index on email with a unique requirement. This is great, but the problem is that I'm trying to run integration tests, and every time I go rake rspec spec/features/...rb, unless I did rake db:test:purge, rake db:test:prepare first, it runs into the unique problem and refuses to run. How do I streamline this?
From the code below, you can see that every time I'm running the test, I'm creating a set of seed data with my before(:all), but since the seed data is always the same, this is driving the uniqueness error.
I'm happy to just put this seed data elsewhere or create it some other way, as long as my test suite is still able to run using this seed data.
describe "how requests should flow" do
before(:all) do
#signup_dd = Signup.create(email:"example#example.com")
end
it "should have 2 inventories and 2 signups to start" do
Signup.count.should == 1
end
describe "request creation" do
before do
Signup.find_by_id(#signup_dd)
visit '/requests/new'
save_and_open_page
fill_in '#borrow__1', :with => 1
click_button
end
it "should affect new Requests and Borrows" do
...
end
end
end
There are two ways to fix this:
Remove the (:all) from the before block. RSpec will execute the before block for each test. It will then undo itself after each test. This is really what you want as it ensures changes made by each test do not bleed into other tests. This is usually the recommended approach.
Keep the (:all), but then add a (:after) block to undo your changes. With the :all argument, the before block is only executed once instead of every time. However, it doesn't automatically undo itself like :each, so the :after block becomes necessary. It is up to you, however, to figure out what needs to go in there. In your example, for instance, it might be:
after(:all) do
Signup.delete_all # Or whatever undoes what the before block did
end
See this blog post regarding the use of the two techniques.
when you use before(:all), you need use after(:all) to clean up the data you created in before(:all)
I'm using RSpec and I want to get result (passed or not), class name, test name (or description) and error message (if present) of each test after once it finished.
So here is a pretty simple code
describe MyClass do
after :each do
#how do I know a test passed, its name, class name and error message?
end
it "should be true" do
5.should be 5
end
it "should be false" do
5.should be 6
end
end
Your suggestions?
There are a few formatters for test output that can show you which tests passed/failed/pending, and it sounds like you are interested in the one called documentation.
In order to make this formatter available for all your rspec files, create a file called .rspec with content:
--color # I just like this one for more easily read output
--formatter documentation
This means that when you run a test suite like you have above you will see:
MyClass
should be true
should be false (Failed - 1)
# Individual output for each error would show up down here
Reference: https://www.relishapp.com/rspec/rspec-core/v/2-11/docs/formatters/custom-formatters
You can get extra information from formatters, but since after hooks are potential points of failure, they don't expose failure information themselves.
See https://www.relishapp.com/rspec/rspec-core/v/2-11/docs/formatters/custom-formatters and https://github.com/rspec/rspec-core/blob/master/lib/rspec/core/formatters/base_formatter.rb