Shared Examples: it_behaves_like vs include_examples context limits
0 reputation · 17 Oct 2024, 20:45 UTC
0 reputation · 17 Oct 2024, 20:45 UTC
RSpec provides two primary methods for applying shared examples to a test suite: it_behaves_like and include_examples. While both facilitate test reuse, they differ in how they handle the execution context and the resulting output in the documentation formatter.
The goal is to maintain a clean test hierarchy while ensuring that shared tests have access to the necessary let declarations and before hooks defined in the host spec. There is uncertainty regarding how include_examples handles variable scoping compared to the nested group created by it_behaves_like, particularly when shared examples rely on implicit dependencies from the parent context.
include_examples affect the resolution of let variables defined in the shared block versus the host spec?include_examples for a large set of shared tests?31275 reputation · 18 Oct 2024, 07:50 UTC
it_behaves_like creates a nested example group; include_examples evaluates the shared example in the host group without nesting.
let resolution differslet definitions are scoped to that nested group and shadow any identically named let from the host group for examples inside the shared block. The host group’s lets remain accessible via super() if needed.let defined in the shared block becomes a host‑group let. If the host already defines a let with the same name, the later definition wins (typically the shared block’s because include_examples is called after the host’s lets). This merges the scopes and can cause unexpected overrides or silent shadowing across the entire host group.Because include_examples does not introduce a nested group, RSpec’s formatter attributes failures to the shared example’s file and line, not to the specific including example group. In large suites where the same shared example is included in many groups, failures are grouped under a single shared‑example entry, making it harder to pinpoint the exact failing context.
# spec/shared_examples_spec.rb
RSpec.shared_examples 'a counter' do
let(:start) { 0 }
it 'starts at zero' do
expect(start).to eq(0)
end
end
RSpec.describe 'using it_behaves_like' do
let(:start) { 5 }
it_behaves_like 'a counter' # nested group → shared let(:start) shadows host → start = 0, passes
end
RSpec.describe 'using include_examples' do
let(:start) { 5 }
include_examples 'a counter' # host group → shared let(:start) overrides host → start = 0, passes
end
Run with rspec --format documentation. Observe:
let(:start) { 0 } takes effect in each case (nested group shadows, flat inclusion overrides).expect(start).to eq(5) to see the difference: the nested group will fail (sees 0), the flat inclusion will also fail (sees 0) because the shared let overrides the host let. To observe isolation, add a host‑only example that checks start; it will see 5 in the first group but 0 in the second.include_examples point to the shared example file, while it_behaves_like adds an extra group level in the output, making the hierarchy clearer.it_behaves_like when you want isolation and clear hierarchical output.include_examples only when you intentionally want the shared code to merge with the host scope (e.g., sharing before hooks that should run in the same context) and you are confident no name clashes exist.Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.