How to stub has_many association in RSpec
I'm try to stub has_many association in RSpec because building records is so complicated. I want to detect which author has science book by using Author#has_science_tag?
.
Model
class Book < ApplicationRecord
has_and_belongs_to_many :tags
belongs_to :author
end
class Tag < ApplicationRecord
has_and_belongs_to_many :books
end
class Author < ApplicationRecord
has_many :books
def has_science_tag?
tags = books.joins(:tags).pluck('tags.name')
tags.grep(/science/i).present?
end
end
RSpec
require 'rails_helper'
RSpec.describe Author, type: :model do
describe '#has_science_tag?' do
let(:author) { create(:author) }
context 'one science book' do
example 'it returns true' do
allow(author).to receive_message_chain(:books, :joins, :pluck).with(no_args).with(:tags).with('tags.name').and_return(['Science'])
expect(author.has_science_tag?).to be_truthy
end
end
end
end
In this case, using receive_message_chain
is good choice? Or stubbing has_many association is bad idea?
ruby rspec
add a comment |
I'm try to stub has_many association in RSpec because building records is so complicated. I want to detect which author has science book by using Author#has_science_tag?
.
Model
class Book < ApplicationRecord
has_and_belongs_to_many :tags
belongs_to :author
end
class Tag < ApplicationRecord
has_and_belongs_to_many :books
end
class Author < ApplicationRecord
has_many :books
def has_science_tag?
tags = books.joins(:tags).pluck('tags.name')
tags.grep(/science/i).present?
end
end
RSpec
require 'rails_helper'
RSpec.describe Author, type: :model do
describe '#has_science_tag?' do
let(:author) { create(:author) }
context 'one science book' do
example 'it returns true' do
allow(author).to receive_message_chain(:books, :joins, :pluck).with(no_args).with(:tags).with('tags.name').and_return(['Science'])
expect(author.has_science_tag?).to be_truthy
end
end
end
end
In this case, using receive_message_chain
is good choice? Or stubbing has_many association is bad idea?
ruby rspec
add a comment |
I'm try to stub has_many association in RSpec because building records is so complicated. I want to detect which author has science book by using Author#has_science_tag?
.
Model
class Book < ApplicationRecord
has_and_belongs_to_many :tags
belongs_to :author
end
class Tag < ApplicationRecord
has_and_belongs_to_many :books
end
class Author < ApplicationRecord
has_many :books
def has_science_tag?
tags = books.joins(:tags).pluck('tags.name')
tags.grep(/science/i).present?
end
end
RSpec
require 'rails_helper'
RSpec.describe Author, type: :model do
describe '#has_science_tag?' do
let(:author) { create(:author) }
context 'one science book' do
example 'it returns true' do
allow(author).to receive_message_chain(:books, :joins, :pluck).with(no_args).with(:tags).with('tags.name').and_return(['Science'])
expect(author.has_science_tag?).to be_truthy
end
end
end
end
In this case, using receive_message_chain
is good choice? Or stubbing has_many association is bad idea?
ruby rspec
I'm try to stub has_many association in RSpec because building records is so complicated. I want to detect which author has science book by using Author#has_science_tag?
.
Model
class Book < ApplicationRecord
has_and_belongs_to_many :tags
belongs_to :author
end
class Tag < ApplicationRecord
has_and_belongs_to_many :books
end
class Author < ApplicationRecord
has_many :books
def has_science_tag?
tags = books.joins(:tags).pluck('tags.name')
tags.grep(/science/i).present?
end
end
RSpec
require 'rails_helper'
RSpec.describe Author, type: :model do
describe '#has_science_tag?' do
let(:author) { create(:author) }
context 'one science book' do
example 'it returns true' do
allow(author).to receive_message_chain(:books, :joins, :pluck).with(no_args).with(:tags).with('tags.name').and_return(['Science'])
expect(author.has_science_tag?).to be_truthy
end
end
end
end
In this case, using receive_message_chain
is good choice? Or stubbing has_many association is bad idea?
ruby rspec
ruby rspec
asked Nov 21 '18 at 12:10
Akira NoguchiAkira Noguchi
3341815
3341815
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
Why dont you make use of FactoryBot associations
?
FactoryBot.define do
# tag factory with a `belongs_to` association for the book
factory :tag do
name { 'test_tag' }
book
trait :science do
name { 'science' }
end
end
# book factory with a `belongs_to` association for the author
factory :book do
title { "Through the Looking Glass" }
author
factory :science_book do
title { "Some science stuff" }
after(:create) do |book, evaluator|
create(:tag, :science, book: book)
end
end
end
# author factory without associated books
factory :author do
name { "John Doe" }
# author_with_science_books will create book data after the author has
# been created
factory :author_with_science_books do
# books_count is declared as an ignored attribute and available in
# attributes on the factory, as well as the callback via the evaluator
transient do
books_count { 5 }
end
# the after(:create) yields two values; the author instance itself and
# the evaluator, which stores all values from the factory, including
# ignored attributes; `create_list`'s second argument is the number of
# records to create and we make sure the author is associated properly
# to the book
after(:create) do |author, evaluator|
create_list(:science_book, evaluator.books_count, authors: [author])
end
end
end
end
This allows you to do:
create(:author).books.count # 0
create(:author_with_science_books).books.count # 5
create(:author_with_science_books, books_count: 15).books.count # 15
So your test becomes:
RSpec.describe Author, type: :model do
describe '#has_science_tag?' do
let(:author_with_science_books) { create(:author_with_science_books, books_count: 1) }
context 'one science book' do
it 'returns true' do
expect(author_with_science_books.has_science_tag?).to eq true
end
end
end
end
And you could also refactor Author#has_science_tag?
:
class Author < ApplicationRecord
has_many :books
def has_science_tag?
books.joins(:tags).where("tags.name ILIKE '%science%'").exists?
end
end
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53411752%2fhow-to-stub-has-many-association-in-rspec%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
Why dont you make use of FactoryBot associations
?
FactoryBot.define do
# tag factory with a `belongs_to` association for the book
factory :tag do
name { 'test_tag' }
book
trait :science do
name { 'science' }
end
end
# book factory with a `belongs_to` association for the author
factory :book do
title { "Through the Looking Glass" }
author
factory :science_book do
title { "Some science stuff" }
after(:create) do |book, evaluator|
create(:tag, :science, book: book)
end
end
end
# author factory without associated books
factory :author do
name { "John Doe" }
# author_with_science_books will create book data after the author has
# been created
factory :author_with_science_books do
# books_count is declared as an ignored attribute and available in
# attributes on the factory, as well as the callback via the evaluator
transient do
books_count { 5 }
end
# the after(:create) yields two values; the author instance itself and
# the evaluator, which stores all values from the factory, including
# ignored attributes; `create_list`'s second argument is the number of
# records to create and we make sure the author is associated properly
# to the book
after(:create) do |author, evaluator|
create_list(:science_book, evaluator.books_count, authors: [author])
end
end
end
end
This allows you to do:
create(:author).books.count # 0
create(:author_with_science_books).books.count # 5
create(:author_with_science_books, books_count: 15).books.count # 15
So your test becomes:
RSpec.describe Author, type: :model do
describe '#has_science_tag?' do
let(:author_with_science_books) { create(:author_with_science_books, books_count: 1) }
context 'one science book' do
it 'returns true' do
expect(author_with_science_books.has_science_tag?).to eq true
end
end
end
end
And you could also refactor Author#has_science_tag?
:
class Author < ApplicationRecord
has_many :books
def has_science_tag?
books.joins(:tags).where("tags.name ILIKE '%science%'").exists?
end
end
add a comment |
Why dont you make use of FactoryBot associations
?
FactoryBot.define do
# tag factory with a `belongs_to` association for the book
factory :tag do
name { 'test_tag' }
book
trait :science do
name { 'science' }
end
end
# book factory with a `belongs_to` association for the author
factory :book do
title { "Through the Looking Glass" }
author
factory :science_book do
title { "Some science stuff" }
after(:create) do |book, evaluator|
create(:tag, :science, book: book)
end
end
end
# author factory without associated books
factory :author do
name { "John Doe" }
# author_with_science_books will create book data after the author has
# been created
factory :author_with_science_books do
# books_count is declared as an ignored attribute and available in
# attributes on the factory, as well as the callback via the evaluator
transient do
books_count { 5 }
end
# the after(:create) yields two values; the author instance itself and
# the evaluator, which stores all values from the factory, including
# ignored attributes; `create_list`'s second argument is the number of
# records to create and we make sure the author is associated properly
# to the book
after(:create) do |author, evaluator|
create_list(:science_book, evaluator.books_count, authors: [author])
end
end
end
end
This allows you to do:
create(:author).books.count # 0
create(:author_with_science_books).books.count # 5
create(:author_with_science_books, books_count: 15).books.count # 15
So your test becomes:
RSpec.describe Author, type: :model do
describe '#has_science_tag?' do
let(:author_with_science_books) { create(:author_with_science_books, books_count: 1) }
context 'one science book' do
it 'returns true' do
expect(author_with_science_books.has_science_tag?).to eq true
end
end
end
end
And you could also refactor Author#has_science_tag?
:
class Author < ApplicationRecord
has_many :books
def has_science_tag?
books.joins(:tags).where("tags.name ILIKE '%science%'").exists?
end
end
add a comment |
Why dont you make use of FactoryBot associations
?
FactoryBot.define do
# tag factory with a `belongs_to` association for the book
factory :tag do
name { 'test_tag' }
book
trait :science do
name { 'science' }
end
end
# book factory with a `belongs_to` association for the author
factory :book do
title { "Through the Looking Glass" }
author
factory :science_book do
title { "Some science stuff" }
after(:create) do |book, evaluator|
create(:tag, :science, book: book)
end
end
end
# author factory without associated books
factory :author do
name { "John Doe" }
# author_with_science_books will create book data after the author has
# been created
factory :author_with_science_books do
# books_count is declared as an ignored attribute and available in
# attributes on the factory, as well as the callback via the evaluator
transient do
books_count { 5 }
end
# the after(:create) yields two values; the author instance itself and
# the evaluator, which stores all values from the factory, including
# ignored attributes; `create_list`'s second argument is the number of
# records to create and we make sure the author is associated properly
# to the book
after(:create) do |author, evaluator|
create_list(:science_book, evaluator.books_count, authors: [author])
end
end
end
end
This allows you to do:
create(:author).books.count # 0
create(:author_with_science_books).books.count # 5
create(:author_with_science_books, books_count: 15).books.count # 15
So your test becomes:
RSpec.describe Author, type: :model do
describe '#has_science_tag?' do
let(:author_with_science_books) { create(:author_with_science_books, books_count: 1) }
context 'one science book' do
it 'returns true' do
expect(author_with_science_books.has_science_tag?).to eq true
end
end
end
end
And you could also refactor Author#has_science_tag?
:
class Author < ApplicationRecord
has_many :books
def has_science_tag?
books.joins(:tags).where("tags.name ILIKE '%science%'").exists?
end
end
Why dont you make use of FactoryBot associations
?
FactoryBot.define do
# tag factory with a `belongs_to` association for the book
factory :tag do
name { 'test_tag' }
book
trait :science do
name { 'science' }
end
end
# book factory with a `belongs_to` association for the author
factory :book do
title { "Through the Looking Glass" }
author
factory :science_book do
title { "Some science stuff" }
after(:create) do |book, evaluator|
create(:tag, :science, book: book)
end
end
end
# author factory without associated books
factory :author do
name { "John Doe" }
# author_with_science_books will create book data after the author has
# been created
factory :author_with_science_books do
# books_count is declared as an ignored attribute and available in
# attributes on the factory, as well as the callback via the evaluator
transient do
books_count { 5 }
end
# the after(:create) yields two values; the author instance itself and
# the evaluator, which stores all values from the factory, including
# ignored attributes; `create_list`'s second argument is the number of
# records to create and we make sure the author is associated properly
# to the book
after(:create) do |author, evaluator|
create_list(:science_book, evaluator.books_count, authors: [author])
end
end
end
end
This allows you to do:
create(:author).books.count # 0
create(:author_with_science_books).books.count # 5
create(:author_with_science_books, books_count: 15).books.count # 15
So your test becomes:
RSpec.describe Author, type: :model do
describe '#has_science_tag?' do
let(:author_with_science_books) { create(:author_with_science_books, books_count: 1) }
context 'one science book' do
it 'returns true' do
expect(author_with_science_books.has_science_tag?).to eq true
end
end
end
end
And you could also refactor Author#has_science_tag?
:
class Author < ApplicationRecord
has_many :books
def has_science_tag?
books.joins(:tags).where("tags.name ILIKE '%science%'").exists?
end
end
edited Nov 21 '18 at 12:53
answered Nov 21 '18 at 12:31


Martin ZinovskyMartin Zinovsky
1,5241816
1,5241816
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53411752%2fhow-to-stub-has-many-association-in-rspec%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown