Rpsec and Factory Girl not cooperating - ruby-on-rails-4

I'm kind of pulling my hair out on this one. I'm trying to test using rspec and Factory Girl (Ubuntu 13.10 and Rails 4). It seems as if Rspec doesn't see any of the Factory Girl stuff. Here's my spec_helper.rb:
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'factory_girl_rails'
require 'rspec/rails'
require 'rspec/autorun'
require 'capybara/rspec'
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
config.use_transactional_fixtures = true
config.infer_base_class_for_anonymous_controllers = false
config.order = "random"
config.color_enabled = true
config.tty = true
config.formatter = :documentation # :progress, :html, :textmate
end
factories.rb:
FactoryGirl.define do
factory :exercise do
name 'New Exercise'
time 3
reps 7
weight 12
weight_unit 'kg'
factory :aerobic_exercise do
name 'Jumping Jacks'
kind 'Cardio/Aerobic'
end
factory :bodyweight_exercise do
name 'Pushups'
kind 'Bodyweight'
end
factory :cardio_exercise do
name 'Jumping Jacks'
kind 'Cardio/Aerobic'
end
factory :lifting_exercise do
name 'BB Shoulder Presses'
kind 'Weight Lifting'
end
end
end
and my failing spec:
#require 'test/spec'
require 'spec_helper'
describe Exercise do
describe 'Exercise properly normalizes values' do
it 'has weight and weight unit if kind is Weight Lifting' do
let(:exercise) { FactoryGirl.create(:lifting_exercise) }
exercise.should be_weight
exercise.time.should be_nil
end
it 'has time but not weight etc. if kind is Cardio' do
let(:exercise) { FactoryGirl.create(:aerobic_exercise) }
exercise.should be_time
exercise.reps.should be_nil
end
end
end
When I run rspec I get this error:
Failure/Error: let(:exercise) { FactoryGirl.create(:lifting_exercise) }
NoMethodError:
undefined method `let' for # <RSpec::Core::ExampleGroup::Nested_1::Nested_1:0x007f2f013b3760>
HELP!(please)

the let method isn't from FactoryGirl, it's from Rspec, and the issue is that let should not be nested within the it, it's meant to be used outside of it.
Given the way that you've written it, I think you should just use a local variable like this:
describe Exercise do
describe 'Exercise properly normalizes values' do
it 'has weight and weight unit if kind is Weight Lifting' do
exercise = FactoryGirl.create(:lifting_exercise)
exercise.should be_weight
exercise.time.should be_nil
end
it 'has time but not weight etc. if kind is Cardio' do
exercise = FactoryGirl.create(:aerobic_exercise)
exercise.should be_time
exercise.reps.should be_nil
end
end
end
Also, given that you've included FactoryGirl::Syntax::Methods in your spec_helper you don't need to prefix everything with FactoryGirl, you can just call it like this:
exercise = create(:lifting_exercise)
I hope that helps!
-Chad

Your issue is not RSpec and FactoryGirl not playing well together.
let is not part of an example's method group. Notice you have the let inside the it block. This should work
#require 'test/spec'
require 'spec_helper'
describe Exercise do
describe 'Exercise properly normalizes values' do
context 'with a lifting exercise' do
let(:exercise) { FactoryGirl.create(:lifting_exercise) }
it 'has weight and weight unit if kind is Weight Lifting' do
exercise.should be_weight
exercise.time.should be_nil
end
end
context 'with an aerobic exercise' do
let(:exercise) { FactoryGirl.create(:aerobic_exercise) }
it 'has time but not weight etc. if kind is Cardio' do
exercise.should be_time
exercise.reps.should be_nil
end
end
end
end
NOTE context is just an alias for describe.

Related

VCR re-recording cassette does not persist

Thanks in advance - i'm recording a controller spec with vcr.
The problem:
The first time I run my spec, it records fine, then I delete my_spec_context_dir, re-run the spec, and no cassette is saved under the cassettes dir!
The gems:
gem 'rspec-rails', '~> 3.2.1'
gem 'webmock', '1.22.3', require: false
gem 'vcr'
gem 'capybara', '~> 2.4.3'
The spec:
#spec/controllers/my_spec.rb
require 'unit_spec_helper'
describe 'description' do
it 'example_name', :vcr do
#instance_var = table_record.attribute
get 'controller_action'
expect(response.status).to eq(200)
expect(response.content_type).to eq 'image/png'
table_record.delete
end
end
What i've tried:
So I look at my configs to make sure vcr should be recording each time its run:
#spec/unit_spec_helper.rb
require 'vcr'
if Rails.env.test?
VCR.configure do |c|
c.ignore_localhost = false
c.hook_into :webmock
c.cassette_library_dir = "spec/support/cassettes"
c.configure_rspec_metadata!
c.allow_http_connections_when_no_cassette = true
c.default_cassette_options = {
allow_playback_repeats: true,
serialize_with: :json,
record: :all,
match_requests_on: [:method, :uri, :headers]
}
c.debug_logger = File.open(Rails.root.join('log/vcr.log'), 'w')
end
end
allow_playback_repeats: true, record: :all - yep, I think thats what I want
Checking the vcr.log, I see that the cassette was recorded:
[Cassette: 'MyController/description/example_name'] Initialized with options: {:record=>:new_episodes, :match_requests_on=>[:method, :uri, :headers], :allow_unused_http_interactions=>true, :serialize_with=>:json, :persist_with=>:file_system, :allow_playback_repeats=>true}
spec/support/cassettes dir is still empty, so i'm clearing the cache before the test... Rails.cache.clear and re-running.
Refreshing the spec/support/cassettes directory, I see its still empty. What do you suppose is keeping the .yml from saving?
Looks to be an issue with RubyMine7 - after a few restarts, it works.

stub node.attribute? in chefspec

I'm trying to create a spec test for the following recipe code:
if node.attribute?(node['tested_cookbook']['some_attribute'])
include_recipe('tested_cookbook::first')
else
include_recipe('tested_cookbook::second')
I have the following spec for this:
require 'spec_helper'
describe 'tested_cookbook::default' do
let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'windows', version: '2008R2') do |node|
node.set['tested_cookbook']['some_attribute'] = "some_value"
end.converge(described_recipe) }
it 'includes recipe iis' do
expect(chef_run).to include_recipe('tested_cookbook::first')
end
end
The problem is that this test will always fail.
How do I properly mock the outcome of 'node.attribute?' ?
Thank you.
I'm not sure you can override the node object in Chefspec without monkey patching, which I think is probably more trouble than it's worth. I really almost never even see node.attribute? used, so it may be somewhat of an anti-pattern. (Do you really care if it was set, vs. if it has a non-nil value or not?)
I would just avoid using attribute? in the first place, e.g.
Recipe:
if node['tested_cookbook'] && node['tested_cookbook']['some_attribute'])
include_recipe('tested_cookbook::first')
else
include_recipe('tested_cookbook::second')
end
Spec:
require 'spec_helper'
describe 'tested_cookbook::default' do
let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'windows', version: '2008R2') do |node|
node.set['tested_cookbook']['some_attribute'] = "some_value"
end.converge(described_recipe) }
it 'includes recipe iis' do
expect(chef_run).to include_recipe('tested_cookbook::first')
end
end
It's common practice to give these attributes a default value, too, so it would be even more idiomatic to say:
attributes/default.rb:
default['tested_cookbook']['some_attribute'] = 'second'
recipe:
include_recipe "tested_cookbook::#{node['tested_cookbook']['some_attribute']}"
And then in your spec, do the same check as before. You're using an attribute to run ::second, but allowing someone to override it to ::first. If you don't like the pattern of actually using the attribute value to include, you could make it a flag and keep your previous if-statement too.

Use rspec to test class methods are calling scopes

I have created rspec tests for my scopes (scope1, scope2 and scope3) and they pass as expected but I would also like to add some tests for a class method that I have which is what is actually called from my controller (the controller calls the scopes indirectly via this class method):
def self.my_class_method(arg1, arg2)
scoped = self.all
if arg1.present?
scoped = scoped.scope1(arg1)
end
if arg2.present?
scoped = scoped.scope2(arg2)
elsif arg1.present?
scoped = scoped.scope3(arg1)
end
scoped
end
It seems a bit redundant to run the same scope tests for each scenario in this class method when I know they already pass so I assume I really only need to ensure that different scopes are called/applied dependant on the args being passed into this class method.
Can someone advise on what this rspec test would look like.
I thought it might be something along the lines of
expect_any_instance_of(MyModel.my_class_method(arg1, nil)).to receive(:scope1).with(arg1, nil)
but that doesn't work.
I would also appreciate confirmation that this is all that's necessary to test in this situation when I've already tested the scopes anyway would be reassurring.
The Rspec code you wrote is really testing the internal implementation of your method. You should test that the method returns what you want it to return given the arguments, not that it does it in a certain way. That way, your tests will be less brittle. For example if you change what scope1 is called, you won't have to rewrite your my_class_method tests.
I would do that by creating a number of instances of the class and then call the method with various arguments and check that the results are what you expect.
I don't know what scope1 and scope2 do, so I made an example where the arguments are a name attribute for you model and the scope methods simply retrieve all models except those with that name. Obviously, whatever your real arguments and scope methods do you should put that in your tests, and you should modify the expected results accordingly.
I used the to_ary method for the expected results since the self.all call actually returns an ActiveRecord association and therefore wouldn't otherwise match the expected array. You could probably use includes and does_not_includes instead of eq, but perhaps you care about the order or something.
describe MyModel do
describe ".my_class_method" do
# Could be helpful to use FactoryGirl here
# Also note the bang (!) version of let
let!(:my_model_1) { MyModel.create(name: "alex") }
let!(:my_model_2) { MyModel.create(name: "bob") }
let!(:my_model_3) { MyModel.create(name: "chris") }
context "with nil arguments" do
let(:arg1) { nil }
let(:arg2) { nil }
it "returns all" do
expected = [my_model_1, my_model_2, my_model_3]
expect_my_class_method_to_return expected
end
end
context "with a first argument equal to a model's name" do
let(:arg1) { my_model_1.name }
let(:arg2) { nil }
it "returns all except models with name matching the argument" do
expected = [my_model_2, my_model_3]
expect_my_class_method_to_return expected
end
context "with a second argument equal to another model's name" do
let(:arg1) { my_model_1.name }
let(:arg2) { my_model_2.name }
it "returns all except models with name matching either argument" do
expected = [my_model_3]
expect_my_class_method_to_return expected
end
end
end
end
private
def expect_my_class_method_to_return(expected)
actual = described_class.my_class_method(arg1, arg2).to_ary
expect(actual).to eq expected
end
end

Explicitly verifying a mocha expectation after it's been called

I am using Mocha::Hooks#mocha_verify in the after_scenario spinach hook which works fine for some cases. However, there are many times when I want to verify an expectation after a value has been updated on a piece of data. For example,
class MyModel < ActiveRecord::Base
after_commit :checked, if: -> (record) { record.previous_changes.key?('checked_at') && record.checked_at? }
def checked
Bus.publish_at(checked_at + 1.day, 'checked', id: id)
end
end
Right now I'm having to set the expectation before the "act" part of the test runs so I have to do something like:
Bus.expects(:publish_at).with(
instance_of(ActiveSupport::TimeWithZone),
'checked',
has_entries(id: #my_model.id)
).once
The test data in #my_model still has nil checked_at because the "act" part of the test hasn't run yet, but I would like to verify that the first parameter is correct. I don't see a way to do this but it would be nice to be able to verify an invocation after the "act" part of the test like:
Bus.verify(:publish_at).with(
#my_model.checked_at + 1.day,
'checked',
has_entries(id: #my_model.id)
).once

Where do I put generator configurations in Rails 4?

I want to define the following:
config.generators.stylesheets = false
config.generators.javascripts = false
config.generators.helper = false
But despite reading Ror's guide on the subject, I don't get where exactly the methods should go.
I think it belongs in config/application.rb:
module MyApp
class Application < Rails::Application
# Lots of stuff
# Generator configuration
config.generators do |g|
g.orm :active_record
g.template_engine :erb
g.test_framework :test_unit, fixture: false
g.stylesheets false
g.helper false
end
end
end