Currently i am learning with the youtube video Efficient Rails Test Driven Development - by Wolfram Arnold
One exercise is:
A Person object has an optional middle_name.
I create a migration to add the middle name to the database
rake g migration AddMiddleNameToPerson middle_name:string
and i write a spec
it "can have a middle name"
But i got no idea how to test this issue
how can i test an optional field
thanks for help
Bulleric
To say that an attribute is 'optional' implies that the instance is valid when the attribute is nil. So:
it "does not require a middle name" do
#person = Person.new
#person.valid?
#person.errors[:middle_name].should_not include("can't be blank")
end
If you're using shoulda then this can be made even simpler:
describe Person do
it { should_not validate_presence_of(:middle_name) }
end
Assuming you are using Factor_girl, create two factories,
person1 = Factory.create(:person, :first_name=> "A")
person2 = Factory.create(:person, :first_name=> "A", :middle_name => "M")
Now, in your test, show that person1 and person2 both get saved to the database.
You could do the same with fixtures, if you are not using factory_girl.
Related
In my controller test, I am testing the correct value is assigned to an instance variable.
When I do
expect(assigns(:conversations)).to eq #user_inbox
RSpec tells me:
Failure/Error: expect(assigns(:conversations)).to eq #user_inbox
expected: #<ActiveRecord::Relation [#<Mailboxer::Conversation id: 4, subject: "Dude, what up?", created_at: "2014-10-21 08:43:50", updated_at: "2014-10-21 08:43:50">]>
got: #<ActiveRecord::Relation [#<Mailboxer::Conversation id: 4, subject: "Dude, what up?", created_at: "2014-10-21 08:43:50", updated_at: "2014-10-21 08:43:50">]>
(compared using ==)
Diff:
I see that there is no difference between the expected and the actual. I would like to know what is causing this test to fail.
ActiveRecord::Relation compares based on the actual relation, not the result set. For example,
User.where(:id => 123) == User.where(:email => "fred#example.com")
will return false, even the query results are both the same, since the actual queries are different.
I suspect that you care much more about the query results rather than how it was composed, in which case you can use to_a to convert the relation to an array of active record objects. Note that Active Record defines equality based only on the value of the id attribute (with a special case for unsaved objects).
Yes, because this is two ActiveRecord::Relation object. Your instance variable is the first one and you create another one called conversations
You should test the number of rows or other property with something like this:
expect(assigns(:conversations).count).to eq #user_inbox.count
Maybe you should change the test strategy.
When your test is hard to write your code is wrong or your test strategy is wrong. I recommend you no test query result in the controller's test.
you should mock your query result
describe 'GET user conversations' do
before do
your_user.stub(:conversations).and_return "foo bar"
end
it 'assigns the conversations of the user' do
get :user_conversation
expect(assigns(:conversations)).to eq your_user.conversations
end
end
or you should test that some_collaborator.should_receive(:some_methods)
describe 'GET user conversations' do
before do
some_collaborator.stub(:conversations)
end
it 'assigns the conversations of the user' do
some_collaborator.should_receive(:conversations)
get :user_conversation
end
end
I'd like to test the validation of a model's attribute with rspec and factory_girl. The 'special' thing is, that one attribute (the name) isn't allowed to start with Numbers or special signs like %,&,$, ...
For testing this it would be great to write only one example like
it "is invalid with name starting by special character" do
["1","$","%"].each do |i|
name = i + "test"
FactoryGirl.build(:tutu, name: name).should_not be_valid
end
end
This work's for the first case but it won't return the result for the whole list. Is it possible to tell rspec not to stop on the error?
Do this instead:
["1","$","%"].each do |i|
it "is invalid with name starting by '#{i}'" do
FactoryGirl.build(:tutu, name: "#{i}test").should_not be_valid
end
end
I'm trying to run a quick rake task on all my Rails models but haven't been able to call them because this piece of code tells me that I can't call the method columns on a string.
I tried classify instead of camelize and it hasn't worked either, tried inserting a class_eval in there as well, but that dosen't seem to work here / don't know too much about it.
task :collect_models_and_field_names => :environment do
models = Dir.glob("#{models_path}/*").map do |m|
m.capitalize.camelize.columns.each { |n| puts n.name }
end
I do know that this worked so I would have manual access to the model if I needed, but I don't really want to do that...
Model.columns.each { |c| puts c.name }
Try
Kernel.const_get(m.classify).columns
classify just changes the string to look like a class -- i.e. with a capital letter and in camelcase, singular.
after using classify to make the string look like a class/model, you need to use constantize, which actually takes the string and converts it into a class.
See:
http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-constantize
You can use something like this:
models = Dir[Rails.root.join("app", "models", "*.rb")].map do |m|
model = File.basename(m, ".rb").classify.constantize
model.columns.each { |n| puts n.name }
end
When testing a model with a foreign key, I'd like to assert the model can't be saved with an inexistent foreign key.
Example class for testing:
class Wheel
belongs_to: car
end
So, a unit test would look like this:
def test "a wheel must belong to an existent car"
#wheel = Wheel.new
#wheel.car_id = INEXISTENT_CAR_ID
assert !#wheel.save
end
What is the best way to find a valid INEXISTENT_CAR_ID (knowing fixtures are loaded with random ids)?
I like Chowlett's approach. But it fails, if there is no record at all, so you might want to write
#wheel.car_id = Car.order("id").last.try(:id).to_i + 1
Not sure if there's an easier way, but you could go for:
#wheel.car_id = Car.find(:last, :order => :id).id + 1
The best way would be to arrange it so that your fixture or your mock actually has ids you can control. Say if you were using factorygirl, you can say:
#car1 = Factory(:car, :id => 1)
#car2 = Factory(:car, :id => 2)
so you are sure that in your test db you only have 2 ids, 1 and 2. That way you can set up that given a Wheel with an id of 3, then it should not save.
As described in this article, I am using automatic associations in fixtures. For example, if a region object has a country id, instead of doing "country_id": 1, I do "country": "USA". "USA" is a label in my countries.yml file, so fixtures knows how to take care of this. However, this only works when you do not specify an ID value for the countries object. So I cannot assign USA's ID to be 1. But if I do not assign it to be 1, it ends up being some large value 8974343...which is kinda strange. Is there a way to get fixtures to auto-generate id's that are not super high? ....or is this ok?
This is how you get an autogenerated id of the fixture label.
Fixtures.identify(:reginald)
Reading the API documentation, this is exactly how autogenerated fixtures are supposed to behave -- if you want to have a specific ID value for a fixture in advance, you should probably just assign it yourself.
If not, well, from the API docs:
The generated ID for a given label is constant, so we can discover any fixture‘s ID without loading anything, as long as we know the label.
Since I don't have enough reputation to comment, this is the actual Rails 4.1 documentation:
http://edgeapi.rubyonrails.org/classes/ActiveRecord/FixtureSet.html#class-ActiveRecord::FixtureSet-label-Fixture+label+interpolation
Under Fixture label interpolation:
monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
The fixture's id comes directly from hashing its name (that's how "we can discover any fixture‘s ID without loading anything, as long as we know the label")
automated test to enforce fixture integrity
class FixtureIntegrityTest < ActiveSupport::TestCase
context "fixture integrity" do
should "work" do
fixtures = Dir["test/fixtures/*.yml"].map do |file|
[file, File.basename(file).sub(/\..*/, "").singularize, YAML.load(ERB.new(File.read(file)).result)]
end
failures = fixtures.reject(&:last).map { |file,*| "#{file} is empty!"}
failures = failures.presence || fixtures.map do |_, klass, content|
content.select{ |_,fixture| fixture["id"] }.map do |name, _|
fixtures.map do |file, _, content|
content.select { |_,fixture| fixture[klass] == name }.map do |_, fixture|
"#{file} uses #{klass}: #{name}, but should use the id!"
end
end
end
end.flatten.compact
assert_equal [], failures
end
end
end