This is one of those ones that makes you think you're going insane...
I have a class Section, and a DraftSection that inherits from it:
(Trimmed for brevity)
class Section
include Mongoid::Document
belongs_to :site
field :name, type: String
end
And
class DraftSection < Section
field :name, type: String, default: "New Section"
end
All simple stuff... console proves (again, trimmed for brevity):
004 > site = Site.first
=> #<Site _id: initech, name: "INITECH">
005 > site.sections.build
=> #<Section _id: 1, site_id: "initech", name: nil>
006 > site.draft_sections.build
=> #<DraftSection _id: 2, site_id: "initech", name: "New Section">
As you can see - the draft section name correctly defaults to "New Section" as it is overridden in the subclass.
Now when I run this spec:
describe "#new" do
it "should return a draft section" do
get 'new', site_id: site.id, format: :json
assigns(:section).should == "Something..."
end
end
Which tests this controller method:
def new
#section = #site.draft_sections.build
respond_with #section
end
Which fails (as expected), but with this:
Failure/Error: assigns(:section).should == "Something..."
expected: "Something..."
got: #<DraftSection _id: 1, site_id: "site-name-4", name: nil> (using ==)
What gives???
Update:
I figured it might be an issue with the different environment settings, so I looked at the mongoid.yml config file and saw this in the options:
# Preload all models in development, needed when models use
# inheritance. (default: false)
preload_models: true
I added it to the test environment settings too, but still no joy :(
Update 2 - the plot thickens...
Thought I'd try loading up the console in the test environment and trying the same as before:
001 > site = Site.first
=> #<Site _id: initech, name: "INITECH">
002 > site.draft_sections.build
=> #<DraftSection _id: 1, site_id: "initech", name: "New Section">
WTF?
Ok, no one's listening, but I'll post the solution here for future reference anyway...
For some reason, some time ago I had a debug session that meant I had left this code in my Spork.each_run block
# Reload all model files when run each spec
# otherwise there might be out-of-date testing
# require 'rspec/rails'
Dir["#{Rails.root}/app/controllers//*.rb"].each do |controller|
load controller
end
Dir["#{Rails.root}/app/models//*.rb"].each do |model|
load model
end
Dir["#{Rails.root}/lib//*.rb"].each do |klass|
load klass
end
This was causing the models to get reloaded on each run of a spec. Not surprisingly, this screwed up the way the classes were set up in memory whilst the specs were running.
Definitely explains why it was such a hard one to debug...
So for future googlers with similar inheritance problems in Rspec only - make sure that there's nothing reloading models anywhere in the test stack.
Related
I have a test where I want to test that a book_group cannot delete when is associated with a book, this test failed but in the application, this feature works fine
describe 'callbacks' do
context 'before_validation' do
include_examples 'examples for strippable attributes', :book_group, :name, :description, :spawn_button_label
end
it 'is not destroyed and returns a base error if there is an book associated to the it' do
error = 'Cannot delete record because dependent books exist'
book_group.books << create(:book)
expect(book_group.destroy).to be false
end
end
I debugged into the test and found that the error is because this query not working as expected
First, I valid that these two models have an association
pry(#<RSpec::ExampleGroups::bookGroup::Callbacks>)> book_group.books
=> [#<book:0x0000563cb3f6eaf0
id: 1,
review_id: 1,
name: "Vernie Becker",
level: "site",
book_group_id: 831812,
author_book_id: nil]
I do the next query but its result is wrong
book_group.books.where(author_book_id: nil).order(id: :desc).first
=> nil
but this query within console working as expected
[4] pry(main)> #book_group.books.where(author_book_id: nil).order(id: :desc).first DEBUG
[2022-04-02T16:16:49.295Z] book Load (0.7ms) SELECT `books`.* FROM `books` WHERE `books`.`book_group_id` = 6 AND `books`.`author_book_id` IS NULL ORDER BY `books`.`id` DESC LIMIT 1
=> #<book:0x000055d2217339c8 id: 261, review_id: 1, name: "Base book site", level: "site", book_group_id: 6, book_template_id: 2, author_book_id: nil]
the book_group is created in this way
def create
ActiveRecord::Base.transaction do
#book_group = bookGroup.new(book_group_params)
#book_group.author_id = params[:author_id]
#book_group.save!
AllowedActorship.create_from_level_scoped_params(
book_group_params,
#book_group
)
render(
json: { message: #book_group },
status: :ok
)
end
end
I already have reset and prepared the bd, I'm not sure why it working so weird, I will say thank you for whatever helped with it.
I'm new to RoR, a real newbie. But I got a project that need RoR for the backend development. And in this project for the API documentation is using Rswag. and I hit a wall when tried to authorizing the endpoints using JWT, which I want to create the user and the JWT token on the global, similar to what Rspec can do.
But I got a problem when trying to assign a global variable into the rswag tests, I already tried using the before(:each), let() and let!() as well, but they still didn't work which working on the Rspec
require "swagger_helper"
RSpec.describe "Profile API", type: :request do
# I also tried to assign the variables before each test cases
#
# let(:user) { FactoryBot.create(:user, role_id: 3, phone_number: "+6285123456799") }
# --- OR ---
# before(:each) do
# #user = FactoryBot.create(:user, role_id: 3, phone_number: "+6285123456799")
# end
path "/api/v1/profiles" do
post "Create new profile" do
tags "Profile"
consumes "application/json"
produces "application/json"
security [ Bearer: {} ]
parameter name: :profile, in: :body, schema: {
type: :object,
properties: {
first_name: {type: :string},
last_name: {type: :string},
},
required: [:first_name]
}
# I've tried put it here so all the test blocks can access it
# let!(:user) { FactoryBot.create(:user, role_id: 3, phone_number: "+6285123456789") }
response 201, "All fields filled" do
let(:"Authorization") { "Bearer #{auth_header(#user)}" }
let(:profile) { {first_name: "John", last_name: "Doe"} }
run_test! do |response|
expect(response_body).to eq({
"first_name" => "John",
"last_name" => "Doe"
})
end
end
end
end
And here is the error that I got when I tried using let() or let!()
and if I tried to use the Before Block, it doesn't return any errors, but it's also didn't get triggered when I put the byebug in the before block.
currently here are the installed version lists:
Ruby version 3.0.0
Rails version 6.1.1
Rspec-rails gem version 4.0.2
Rswag gem version 2.3.2
And here is the closest answer I can found in the issues, but this doesn't resolve the problem I'm facing now
https://github.com/rswag/rswag/issues/168#issuecomment-454797784
Any bits of help is appreciated, thank you :smile:
I three models Comment, User and Project. Project and Comment need to point to other objects in order to be valid. For example, a comment needs to point to an author (user) and a project.
The associated fixture files look like this:
# comments.yml
test_comment:
author: users(:test_user)
project: projects(:test_project)
# users.yml
test_user:
name: 'test user'
# projects.yml
test_project:
name: 'Test'
description: 'This is a test'
owner: users(:test_user)
However, I've found that my fixtures are probably set up incorrectly. Rails returns false if I try to save the comment:
assert_equal true, comments(:test_comment)
#=> false
I can see that there are foreign keys for a project and author:
=> #<Comment:0x00007f9b1661f3d8
id: 137725605,
body: "",
project_id: 745075726,
author_id: "31ceee04-5307-5059-91db-0dc2068a780c",
created_at: Fri, 22 Feb 2019 13:17:58 UTC +00:00,
updated_at: Fri, 22 Feb 2019 13:17:58 UTC +00:00>
But when I interrogate them, Rails returns nil.
> comments(:test_comment).author
=> nil
> comments(:test_comment).project
=> nil
I expected that one would return users(:test_user) and the other would return projects(:test_project). I thought perhaps I needed to use ERB in my yaml:
test_comment:
author: <%= users(:test_user) %>
project: <%= projects(:test_project) %>
But that results is a stream of errors when I run my tests:
NoMethodError: undefined method `users' for #<#<Class:0x00007f9b17692ff8>:0x00007f9b17692dc8>
What do I need to do to point fixtures to other fixtures? Can it be done? What have I done wrong?
In the Rails guide on Testing with YAML fixtures, you can see that you don't need users(:test_user) to refer to some other object. Instead, you can simply write test_user:
# comments.yml
test_comment:
author: test_user
project: test_project
# users.yml
test_user:
name: 'test user'
# projects.yml
test_project:
name: 'Test'
description: 'This is a test'
owner: test_user
Hope this helps!
I have two models:
class Post
include MongoMapper::Document
many :comments
key :content, String
end
and
class Comment
include MongoMapper::Document
belongs_to :post
key :post_id, ObjectId
key :content, String
end
in a rails console session I can find all Posts:
Post.all # -> [#<Post _id: BSON::ObjectId('519b0b613…
and all comments associated with a Post:
post = Post.first # -> #<Post _id: BSON::ObjectId('519b0b613e477b…
post.comments # -> [#<Comment _id: BSON::ObjectId('519d14f93e…
however, the following query strangely returns an empty array
Comment.all # -> []
Why? How can I get a list of all comments independently of the posts?
With models exactly as per your post, it works for me as shown in the the following test,
running rails (3.2.13), mongo_mapper (0.12.0), mongo (1.6.4). Next time please post the full minimal script, you probably just have a simple error.
test/unit/post_test.rb
require 'test_helper'
class PostTest < ActiveSupport::TestCase
def setup
Post.delete_all
Comment.delete_all
end
test "post and comment" do
post = Post.create(:content => 'Twas brillig')
comment = Comment.create(:post_id => post.id, :content => 'and the slythy toves')
post.comments << comment
assert_equal 1, Post.count
assert_equal 1, Comment.count
puts
puts "all posts: #{Post.all.inspect}"
puts "first post comments: #{Post.first.comments.inspect}"
puts "all comments: #{Comment.all.inspect}"
end
end
$ rake test
Run options:
# Running tests:
[1/1] PostTest#test_post_and_comment
all posts: [#<Post _id: BSON::ObjectId('51ddb56a7f11ba9bbf000001'), content: "Twas brillig">]
first post comments: [#<Comment _id: BSON::ObjectId('51ddb56a7f11ba9bbf000002'), content: "and the slythy toves", post_id: BSON::ObjectId('51ddb56a7f11ba9bbf000001')>]
all comments: [#<Comment _id: BSON::ObjectId('51ddb56a7f11ba9bbf000002'), content: "and the slythy toves", post_id: BSON::ObjectId('51ddb56a7f11ba9bbf000001')>]
Finished tests in 0.036030s, 27.7546 tests/s, 0.0000 assertions/s.
1 tests, 0 assertions, 0 failures, 0 errors, 0 skips
ruby -v: ruby 2.0.0p195 (2013-05-14 revision 40734) [x86_64-darwin12.3.0]
When adding a key to an existing model (with existing data) via MongoMapper, I can create new documents with the new key but when trying to access existing documents using that same key it fails stating that it's an "undefined method."
I was wondering if anyone had any insight.
Thanks in advance!
(Yes, these examples are truncated.)
- model.rb -
key :key_1
key :key_2
- would return -
#<Model _id: BSON::ObjectID('4ba821abebddb9094c000001'), key_1: "test", key_2: "test">
- model.rb (updated version) -
key :key_1
key :key_2
key :key_3
- would still only return -
#<Model _id: BSON::ObjectID('4ba821abebddb9094c000001'), key_1: "test", key_2: "test">
- but if a new doc is created -
#<Model _id: BSON::ObjectID('4ba821abebddb9094c000001'), key_1: "test", key_2: "test">
#<Model _id: BSON::ObjectID('7ba131abedaab9094c007482'), key_1: "test", key_2: "test", key_3: "test">
This would be fine except for the fact that I receive a method undefined error when trying to access :key_3 for the first document.
Rails 2.3.4
MongoMapper 0.7.4
I'm not seeing this behavior at all, even when interacting with an object instantiated before I updated the class. When running the following in irb, I have no errors:
>> gem 'mongo_mapper', '0.7.4'
=> true
>> require 'mongo_mapper'
=> true
>> MongoMapper.database = 'test'
=> "test"
>> class Foo
>> include MongoMapper::Document
>> key :something
>> end
=> #<MongoMapper::Plugins::Keys::Key:0x101f8f938 #default_value=nil, #type=nil, #name="something", #options={}>
>> f = Foo.new(:something => 'thing')
=> #<Foo _id: ObjectID('4c4dc9af712337447c000001'), something: "thing">
>> f.save
=> true
>> f
=> #<Foo _id: ObjectID('4c4dc9af712337447c000001'), something: "thing">
>> class Foo
>> key :something_else
>> end
=> #<MongoMapper::Plugins::Keys::Key:0x101f6ad90 #default_value=nil, #type=nil, #name="something_else", #options={}>
>> f
=> #<Foo something_else: nil, _id: ObjectID('4c4dc9af712337447c000001'), something: "thing">
>> f.something_else
=> nil
Since it seems like you're having an unusual problem, more details of your use-case would be helpful. Could you please give us a more complete code example? If you've got proprietary stuff in the code that's failing, pare it down to the minimum case required to still have it fail and post the complete declarations of your models and the code you're using to access them.
Use the set command...
#model.set(:key_3 => "VALUE...")
#model.reload
#model.key_3 # => "VALUE..."
#model.save
This code will create a new field for your model, confirm that you already did defined with the new key:
key :key_3
Enjoy,