Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customize context inclusion with let_it_be or AnyFixture #158

Open
Systho opened this issue Aug 27, 2019 · 5 comments
Open

Customize context inclusion with let_it_be or AnyFixture #158

Systho opened this issue Aug 27, 2019 · 5 comments

Comments

@Systho
Copy link

Systho commented Aug 27, 2019

With Rspec, there is an idiomatic way to customize context inclusion by providing a block : https://relishapp.com/rspec/rspec-core/docs/example-groups/shared-context#declare-a-shared-context,-include-it-with-%60include-context%60-and-extend-it-with-an-additional-block

This will allow to write thinks like

# with_an_invoice.rb
shared_context "with an invoice" do
  let(:invoice_owner){ raise "you must provide an invoice owner when including this context" }
end
....
# actual spec
include_context("with an user owning resources")# this context exposes a complex let(:user) 
include_context("with an invoice") do # this context exposes a complex let(:invoice)
  let(:invoice_owner){ user } # here I am using the previous user to customize the invoice
end

When following this pattern with let_it_be , the customization block is added to the list of before_all, it does not override the previous one.

How do you handle this scenario ?

@palkan
Copy link
Collaborator

palkan commented Aug 30, 2019

Do I understand correctly: if you replace let with let_it_be in both places in the example above, it doesn’t work as expected and invoice_user != user?

So, you suggest to make it possible to override let_it_be within the same context?
Like this:

describe “smth” do
  let_it_be(:user) { create(:user, name: “A”) }
  let_it_be(:post) { create(:post, user: user) }
  
  let_it_be(:user) { create(:user, name: “B”) }

  specify do
    expect(user.name).to eq “B”
    expect(post.user).to eq user
    expect(User.where(name: “A”).any?).to eq false
  end
end

@palkan palkan added the awaiting response Awaiting reporter's response label Aug 30, 2019
@Systho
Copy link
Author

Systho commented Aug 30, 2019

Yes, that is one way of doing it and I think it is the correct way because this would make let_it_be behave more similarly to let

I think it is ok if the user "A" exists though, it matches the semantic of let_it_be which is closer to let! than let.

The main usage for me would be using it include_context but it boils down to allow overriding let_it_be in a nested context I think.

describe “smth” do
  let_it_be(:user) { create(:user, name: “A”) }

  context "with a user named B" do
    let_it_be(:user) { create(:user, name: “B”) }
    let_it_be(:post) { create(:post, user: user) }
 
    specify do
      expect(user.name).to eq “B”
      expect(post.user).to eq user
      expect(User.where(name: “A”).any?).to eq true # user record exists but is not associated with post
    end
  end
end

Does it make sense ?

@palkan
Copy link
Collaborator

palkan commented Sep 2, 2019

The example you provided should work, post should use the user "B".

The following won't work:

describe “smth” do
  let_it_be(:user) { create(:user, name: “A”) }
  let_it_be(:post) { create(:post, user: user) }

  context "with a user named B" do
    let_it_be(:user) { create(:user, name: “B”) }
 
    specify do
      expect(user.name).to eq “B”
      expect(post.user).to eq user
      expect(User.where(name: “A”).any?).to eq true # user record exists but is not associated with post
    end
  end
end

@Systho
Copy link
Author

Systho commented Sep 2, 2019

but it would work with regular let and let! right ?

the "problem" is that let_it_be block is added to a list of BeforeAll hooks, while let block overrides the previous definition (if any)

For the moment, you explanation allowed me to make my suite work by introducing new contexts but I think my previous way of using context was more like this (which doe snot work) :

describe “smth” do
    context "with a user named B" do
      # result of include_context("with a post"){  let_it_be(:author) { create(:user, name: “B”) } }
      let_it_be(:author) { create(:user, name: “A”) } # this is inside the context file
      let_it_be(:post) { create(:post, user: user) } # this is inside the context file
      let_it_be(:author) { create(:user, name: “B”) } # this is the customization block
 
    specify do
      expect(user.name).to eq “B”
      expect(post.author).to eq user
      expect(User.where(name: “A”).any?).to eq true # user record exists but is not associated with post
    end
  end
end

@palkan palkan added enhancement and removed awaiting response Awaiting reporter's response labels Sep 3, 2019
@palkan
Copy link
Collaborator

palkan commented Sep 3, 2019

Overriding let_it_be definitions within the same context is something we can implement, I think.
Dealing with nested let_it_be is a bit more complicated (though not impossible).

Will think about it during the work on the next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants