Rails 4, rspec 3: model validation test - validation

I have an organization object that has attributes name, doing_business_as. I need to validate that the name is not the same as doing_business_as.
# app/models/organization.rb
class Organization < ActiveRecord::Base
validate :name_different_from_doing_business_as
def name_different_from_doing_business_as
if name == doing_business_as
errors.add(:doing_business_as, "cannot be same as organization name")
end
end
end
I have a matching rspec file that verifies this:
# spec/models/organization_spec.rb
require "rails_helper"
describe Organization do
it "does not allow NAME and DOING_BUSINESS_AS to be the same" do
organization = build(:organization, name: "same-name", doing_business_as: "same-name")
expect(organization.errors[:doing_business_as].size).to eq(1)
end
end
When I run the spec, however, it fails and this is what I get:
$ rspec spec/models/organization_spec.rb
Organization
does not allow NAME and DOING_BUSINESS_AS to be the same (FAILED - 1)
Failures:
1) Organization validations does not allow NAME and DOING_BUSINESS_AS to be the same
Failure/Error: expect(organization.errors[:doing_business_as].size).to eq(1)
expected: 1
got: 0
(compared using ==)
# ./spec/models/organization_spec.rb:113:in `block (3 levels) in <top (required)>'
Finished in 0.79734 seconds (files took 3.09 seconds to load)
10 examples, 1 failure
Failed examples:
rspec ./spec/models/organization_spec.rb:110 # Organization validations does not allow NAME and DOING_BUSINESS_AS to be the same
I was expecting the spec to pass and ensure that the 2 attributes cannot be the same. In the Rails console I can mimic the expected behavior, but I can't seem to get the spec to "fail" successfully.
I also checked via the Rails Console that it works as expected:
$ rails c
> o = Organization.new(name: "same", doing_business_as: "same")
> o.valid?
=> false
> o.errors[:doing_business_as]
=> ["cannot be the same as organization name"]
So I know the functionality is there, but I can't get a workable test...

You need to use build method instead of create method.
# spec/models/organization_spec.rb
require "rails_helper"
describe Organization do
it "does not allow NAME and DOING_BUSINESS_AS to be the same" do
organization = build(:organization, name: "same-name", doing_business_as: "same-name")
organization.valid?
expect(organization.errors[:doing_business_as].size).to eq(1)
end
end
or
# spec/models/organization_spec.rb
require "rails_helper"
describe Organization do
it "does not allow NAME and DOING_BUSINESS_AS to be the same" do
organization = build(:organization, name: "same-name", doing_business_as: "same-name")
expect(organization).to be_invalid
end
end

Related

How to test a Ruby Roda app using RSpec to pass an argument to app.new with initialize

This question probably has a simple answer but I can't find any examples for using Roda with RSpec3, so it is difficult to troubleshoot.
I am using Marston and Dees "Effective Testing w/ RSpec3" book which uses Sinatra instead of Roda. I am having difficulty passing an object to API.new, and, from the book, this is what works with Sinatra but fails with a "wrong number of arguments" error when I substitute Roda.
Depending on whether I pass arguments with super or no arguments with super(), the error switches to indicate that the failure occurs either at the initialize method or in the call to Rack::Test::Methods post in the spec.
I see that in Rack::Test, in the Github repo README, I may have to use Rack::Builder.parse_file("config.ru") but that didn't help.
Here are the two errors that rspec shows when using super without brackets:
Failures:
1) MbrTrak::API POST /users when the user is successfully recorded returns the user id
Failure/Error: post '/users', JSON.generate(user)
ArgumentError:
wrong number of arguments (given 1, expected 0)
# ./spec/unit/app/api_spec.rb:21:in `block (4 levels) in <module:MbrTrak>'
And when using super():
1) MbrTrak::API POST /users when the user is successfully recorded returns the user id
Failure/Error: super()
ArgumentError:
wrong number of arguments (given 0, expected 1)
# ./app/api.rb:8:in `initialize'
# ./spec/unit/app/api_spec.rb:10:in `new'
# ./spec/unit/app/api_spec.rb:10:in `app'
# ./spec/unit/app/api_spec.rb:21:in `block (4 levels) in <module:MbrTrak>'
This is my api_spec.rb:
require_relative '../../../app/api'
require 'rack/test'
module MbrTrak
RecordResult = Struct.new(:success?, :expense_id, :error_message)
RSpec.describe API do
include Rack::Test::Methods
def app
API.new(directory: directory)
end
let(:directory) { instance_double('MbrTrak::Directory')}
describe 'POST /users' do
context 'when the user is successfully recorded' do
it 'returns the user id' do
user = { 'some' => 'user' }
allow(directory).to receive(:record)
.with(user)
.and_return(RecordResult.new(true, 417, nil))
post '/users', JSON.generate(user)
parsed = JSON.parse(last_response.body)
expect(parsed).to include('user_id' => 417)
end
end
end
end
end
And here is my api.rb file:
require 'roda'
require 'json'
module MbrTrak
class API < Roda
def initialize(directory: Directory.new)
#directory = directory
super()
end
plugin :render, escape: true
plugin :json
route do |r|
r.on "users" do
r.is Integer do |id|
r.get do
JSON.generate([])
end
end
r.post do
user = JSON.parse(request.body.read)
result = #directory.record(user)
JSON.generate('user_id' => result.user_id)
end
end
end
end
end
My config.ru is:
require "./app/api"
run MbrTrak::API
Well roda has defined initialize method that receives env as an argument which is being called by the app method of the class. Looks atm like this
def self.app
...
lambda{|env| new(env)._roda_handle_main_route}
...
end
And the constructor of the app looks like this
def initialize(env)
When you run your config.ru with run MbrTrack::API you are actually invoking the call method of the roda class which looks like this
def self.call(env)
app.call(env)
end
Because you have redefined the constructor to accept hash positional argument this no longer works and it throws the error you are receiving
ArgumentError:
wrong number of arguments (given 0, expected 1)
Now what problem are you trying to solve, if you want to make your API class configurable one way to go is to try out dry-configurable which is part of the great dry-ruby gem collection.
If you want to do something else feel free to ask.
It has been a long time since you posted your question so hope you will still find this helpful.

Why does changing the order of 'it' and 'subject' in RSpec change my test result?

The class being tested qa.rb contains the code:
class QA
def initialize(bugs: 0)
#bugs = bugs
end
def speak
"Hello!"
end
def happy?
#bugs > 0
end
def debug
#bugs = 0
end
end
The RSpec file qa_spec.rb contains the code:
require 'rspec'
require_relative 'qa'
RSpec.describe QA do
describe '#happy?' do
context 'when bugs are more than 0' do
it 'returns true' do
subject { described_class.new(bugs: 1) }
expect(subject).to be_happy
end
end
end
end
The test fails when I run it, and gives me this error:
PS C:\Users\Jobla\repos\TDD> rspec qa_spec.rb
F
Failures:
1) QA#happy? when bugs are more than 0 returns true
Failure/Error: expect(subject).to be_happy
expected `#<QA:0x2e0d640 #bugs=0>.happy?` to return true, got false
# ./qa_spec.rb:9:in `block (4 levels) in <top (required)>'
Finished in 0.02999 seconds (files took 0.16995 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./qa_spec.rb:7 # QA#happy? when bugs are more than 0 returns true
However, when I edit qa_spec.rb and I swap the it and subject lines, the test suddenly passes:
require 'rspec'
require_relative 'qa'
RSpec.describe QA do
describe '#happy?' do
context 'when bugs are more than 0' do
subject { described_class.new(bugs: 1) } #swapped with line below
it 'returns true' do #swapped with line above
expect(subject).to be_happy
end
end
end
end
Tests pass:
PS C:\Users\Jobla\repos\TDD> rspec qa_spec.rb
.
Finished in 0.01003 seconds (files took 0.17993 seconds to load)
1 example, 0 failures
Please could someone explain why does swapping the it and subject lines change the result of the test?
subject is designed to be set in context or describe block, but not in it.
If you do not set subject before it then subject would be set automatically by calling new without parameters on described_class. bugs will be set to default 0. After that, you call it with a block subject { described_class.new(bugs: 1) } inside it, it's the same as if you call described_class.new { described_class.new(bugs: 1) } because subject inside it is an instance of QA class.

Tests fail with record creation in Rails 5

I am building an app in school and I am running into this error. As of right now the app walk through was started in rails 4.2.6, and I am running 5.0.0.1.
The error is:
Failures:
1) Post Creation can be created
Failure/Error: expect(#post).to be_valid
expected #<Post id: nil, date: "2016-12-20", rationale: "Anything", created_at: nil, updated_at: nil, user_id: nil> to be valid, but got errors: User must exist
# ./spec/models/post_spec.rb:10:in `block (3 levels) in <top (required)>'
Finished in 0.65569 seconds (files took 2.19 seconds to load)
10 examples, 1 failure
Failed examples:
rspec ./spec/models/post_spec.rb:9 # Post Creation can be created
My code is as follows. I have compared to the repo on the walk-through and it matches perfectly. What am i missing?
require 'rails_helper'
RSpec.describe Post, type: :model do
describe "Creation" do
before do
#post = Post.create(date: Date.today, rationale: "Anything")
end
it "can be created" do
expect(#post).to be_valid
end
it "cannot be created without a date and rationale" do
#post.date = nil
#post.rationale = nil
expect(#post).to_not be_valid
end
end
end
Rails 5 differs from Rails 4 in that when you have a belongs_to relation, Rails 5 will automatically validate the presence of the associated object, even without you adding any validations.
Probably your Post model belongs to a User. Because of this, you need to create a user in your test setup, or the validation will fail:
describe "Creation" do
before do
#user = User.create( ... )
#post = Post.create(date: Date.today, rationale: "Anything", user: #user)
end
it "can be created" do
expect(#post).to be_valid
end
it "cannot be created without a date and rationale" do
#post.date = nil
#post.rationale = nil
expect(#post).to_not be_valid
end
end

Setting FactoryGirl and use it in Grape and RSpec?

In my Grape ruby project. I'm separating my models to a gem so I can use it between my ruby projects.
Now the problem is that with my activerecords, let's say I'm dealing with User model, now it looks something like this:
module MyApp
module Core
class User < ActiveRecord::Base
self.table_name = 'users'
end
end
end
And I'm using Factory girl something like this:
FactoryGirl.define do
factory :user do
first_name { Faker::Name.first_name }
last_name { Faker::Name.last_name }
email { Faker::Internet.email }
password { "12345678" }
password_confirmation { "12345678" }
end
end
And let's say I have the following rspec test:
require 'spec_helper'
describe MyApp::Core::User do
it "has name assigned" do
user = build(:member, first_name: 'Eki', last_name: 'Eqbal')
expect(user.first_name).to eq('Eki')
expect(user.last_name).to eq('Eqbal')
end
end
And when I try to run the test:
⇒ bundle exec rspec spec/unit/users_spec.rb
warning: parser/current is loading parser/ruby22, which recognizes
warning: 2.2.3-compliant syntax, but you are running 2.2.0.
Run options: include {:focus=>true}
All examples were filtered out; ignoring {:focus=>true}
F
Failures:
1) MyApp::Core::User has name assigned
Failure/Error: user = build(:user, first_name: 'Eki', last_name: 'Eqbal')
NameError:
uninitialized constant User
# ./spec/unit/users_spec.rb:37:in `block (2 levels) in <top (required)>'
Finished in 0.14788 seconds
1 example, 1 failure
Failed examples:
Instead of:
factory :user do
Try:
factory :user, class: MyApp::Core::User do
FactoryGirl guesses the class name based on the factory name, so if it's in a module like that, it won't find it.

Ruby basic RSpec test does not pass

I'm not able to understand why the following Rspec test does not pass -
require "rspec"
require_relative "file-to-be-tested"
describe Customer do
it "is valid with firstname" do
customer = Customer.new("handy")
expect(customer).to be_valid
end
end
for the corresponding Class definition -
class Customer
attr_reader :firstname
def initialize(firstname)
#firstname = firstname
end
end
these two code snippets are in separate files in the same folder, so when i run ~rspec <first-filename> in the terminal, I get the following error -
F
Failures:
1) Customer is valid with firstname
Failure/Error: expect(customer).to be_valid
expected #<Customer:0x007f90e50f3110> to respond to `valid?`
# ./poodr/poodr_rspec.rb:8:in `block (2 levels) in <top (required)>'
Finished in 0.00551 seconds (files took 0.52876 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./poodr/poodr_rspec.rb:6 # Customer is valid with firstname
be_valid is an rspec-rails method, but it looks like you're using just straight rspec. you could do something like:
require "rspec"
require_relative "file-to-be-tested"
describe Customer do
it "is valid with firstname" do
expect { Customer.new('handy') }.to_not raise_error
end
end
What are you expecting the to be_valid test to do? The issue is that the Customer class has no method called valid? which your test is trying to test.
A hack to move your test along if your doing test driven development:
class Customer
def valid?
true
end
end
You now have a method called valid and your test will pass. Obviously it shouldn't always be true so your next step would be to expand the definition of valid?. What check needs to be done to know if a customer is valid or not?

Resources