.skills/discourse-writing-rspec-tests/references/rspec-style-guide.md
Adapted from: https://rspec.rubystyle.guide/
No blank lines immediately after feature, context, or describe declarations
One blank line between separate describe/context blocks; no blank line before closing end
One blank line after let, subject, and before/after declarations before subsequent blocks
Group let/subject together, separate from before/after hooks with blank lines
Surround multi-line declarations with blank lines — when a let, let!, fab!, or subject uses a do...end or multi-line { } body, put a blank line above and below it, even when adjacent declarations are single-line. Single-line declarations can still pack together, but a multi-line block always breathes on both sides:
# bad — multi-line blocks stacked against neighbors
fab!(:author)
fab!(:topic) do
Fabricate(:topic, user: author, title: "Hello")
end
fab!(:reply) do
Fabricate(:post, topic: topic, user: author)
end
fab!(:tag)
# good
fab!(:author)
fab!(:topic) do
Fabricate(:topic, user: author, title: "Hello")
end
fab!(:reply) do
Fabricate(:post, topic: topic, user: author)
end
fab!(:tag)
One blank line before and after each it/specify block
Blank lines between logical chunks within an example — separate setup, action, and assertion for readability
subject → fab!/let!/let → before → aftercontext blocks to organize test conditions; avoid conditional logic in example descriptionsfab! for shared test data, let for computed values or non-persisted objectslet over instance variables — let(:name) { "John" } not before { @name = "John" }:each/:example scope on before/after/around hooks (they're the default):context over :all when specifying hook scope:context-scoped hooks to prevent state leakageaggregate_failures tag for multiple expectations; apply consistentlysubject to eliminate repetition when multiple tests reference the same objectsubject(:article) { ... } not anonymous subject { ... } (unless using is_expected)specify for tests without descriptions; use it for described exampleschange instead of depending on shared statefreeze_time — don't stub Time.now or Date.todaystub_const or Class.newexpect { do_something }.to change(...) not implicit block subjectscontext "when the user is logged in"it description rather than deeply nesting context blocks; limit nesting to 2 levels maxit "returns the summary" not it "should return the summary".method_name for class methods, #method_name for instance methodsexpect syntax — never shouldexpect(article).to be_published not expect(article.published?).to be trueexpect(title).to include "lengthy" not expect(title.include?("lengthy")).to be truebe — use be_truthy, be_nil, be_an(Type) etc.any_instance_of — mock injected dependencies directlyhave_no_selector not to_not have_selector