Test Guidelines
getsentry/sentry-dart
Enforce Sentry Dart/Flutter SDK test conventions for naming, structure, and fixtures.
Bike Index's RSpec testing conventions — how to structure specs with context and let, what kinds of tests to write, and what to avoid (mocks, controller specs, testing private methods).
$ npx skills add bikeindex/bike_index --skill rspec-testing -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install bikeindex/bike_index rspec-testing --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/bikeindex/bike_index.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/rspec-testing .claude/skills/rspec-testing && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "rspec-testing" agent skill from https://github.com/bikeindex/bike_index/tree/main/.claude/skills/rspec-testing into .claude/skills/rspec-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "rspec-testing", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/bikeindex/bike_index/tree/main/.claude/skills/rspec-testingType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add bikeindex/bike_index --skill rspec-testing -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install bikeindex/bike_index rspec-testing --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/bikeindex/bike_index.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/rspec-testing .agents/skills/rspec-testing && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "rspec-testing" agent skill from https://github.com/bikeindex/bike_index/tree/main/.claude/skills/rspec-testing into .agents/skills/rspec-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "rspec-testing", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add bikeindex/bike_index --skill rspec-testing -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install bikeindex/bike_index rspec-testing --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/bikeindex/bike_index.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/rspec-testing .cursor/skills/rspec-testing && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "rspec-testing" agent skill from https://github.com/bikeindex/bike_index/tree/main/.claude/skills/rspec-testing into .cursor/skills/rspec-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "rspec-testing", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/bikeindex/bike_index.git --path .claude/skills/rspec-testing--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add bikeindex/bike_index --skill rspec-testing -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install bikeindex/bike_index rspec-testing --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/bikeindex/bike_index.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/rspec-testing .gemini/skills/rspec-testing && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "rspec-testing" agent skill from https://github.com/bikeindex/bike_index/tree/main/.claude/skills/rspec-testing into .gemini/skills/rspec-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "rspec-testing", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install bikeindex/bike_index rspec-testingInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add bikeindex/bike_index --skill rspec-testing -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/bikeindex/bike_index.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/rspec-testing .github/skills/rspec-testing && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "rspec-testing" agent skill from https://github.com/bikeindex/bike_index/tree/main/.claude/skills/rspec-testing into .github/skills/rspec-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "rspec-testing", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add bikeindex/bike_index --skill rspec-testing -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install bikeindex/bike_index rspec-testing --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/bikeindex/bike_index.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/rspec-testing .opencode/skills/rspec-testing && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "rspec-testing" agent skill from https://github.com/bikeindex/bike_index/tree/main/.claude/skills/rspec-testing into .opencode/skills/rspec-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "rspec-testing", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
rspec-testingBike Index's RSpec testing conventions — how to structure specs with context and let, what kinds of tests to write, and what to avoid (mocks, controller specs, testing private methods).
Rspec Testing is an agent skill from bikeindex/bike_index. Bike Index's RSpec testing conventions — how to structure specs with context and let, what kinds of tests to write, and what to avoid (mocks, controller specs, testing private methods). Trigger when writing or modifying any spec.rb file, adding test coverage for new code, refactoring tests, or designing the test layout for a new feature. Includes Good/Bad examples of the project's preferred style.
Its SKILL.md is about 4.4k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Testing & QA, covering Test coverage and Refactoring. The repository describes itself as: All the code for Bike Index, because we love you. The licence is AGPL-3.0.
Read from SKILL.md and the folder at commit ba388d8. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
gitbundleFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use git, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
STRIPE_SECRET_KEYFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Rspec Testing loads about 4.4k tokens when it runs. Until then it costs about 106 tokens; SKILL.md has 1,885 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from bikeindex/bike_index at commit ba388d8, republished under its AGPL-3.0 licence (© bikeindex). 1,885 words, ~4,352 tokens.
.claude/skills/rspec-testing/SKILL.md (or your agent's skills folder).This project uses RSpec. All business logic should be tested.
AGENTS.md has the rule. When something fails outside the files you changed, re-run that spec file on its own first: failing alone means it's real and gets fixed, never excused as pre-existing; passing alone is the fixing-flaky-failures skill, whose first rule is that you can't reach for a retry, a looser matcher, or a deleted assertion to make it green.
rtk's rspec wrapper reports a summary in place of the run's output, which costs you two different things. A puts added to a scratch spec to inspect a value vanishes and the run reads as an ordinary pass — redirect it (bundle exec rspec spec/foo_spec.rb > tmp/probe.log 2>&1) or write to a file from inside the example, as fixing-flaky-failures does for browser-side values. A failure keeps its ExpectationNotMetError: line but loses the expected/got values under it, so re-running the one example tells you no more than the first run did; rtk proxy bundle exec rspec <file>:<line> is what shows them. --format documentation rescues neither.
tap to bundle factory creation with follow-up setup. Create the record in let/let!, then do the follow-up work on its own line (a separate statement, or a before block). One thing per line reads better and keeps the factory call clean.let!(:bike_transferred) { FactoryBot.create(:bike, :with_ownership_claimed, user:) }
before { BikeServices::OwnershipTransferer.find_or_create(bike_transferred, updator: user, new_owner_email: "new@example.com") }let!(:bike_transferred) do
FactoryBot.create(:bike, :with_ownership_claimed, user:).tap do |bike|
BikeServices::OwnershipTransferer.find_or_create(bike, updator: user, new_owner_email: "new@example.com")
end
endA request spec reads session and assigns freely, but a write doesn't outlive the call — the next request rebuilds the session from the cookie. There is no controller spec to fall back on; spec/controllers/ is gone.
So drive the request that sets the key: /oauth/authorize?partner=…&company=… for partner/company (arrive_via_partner in :existing_doorkeeper_app), /session/new?return_to=… for return_to, a bike's recovery link for recovery_link_token, a page render as an organization member for passive_organization_id. Where a param reaches the same code — return_to is read from params too — prefer the param over a priming request. spec/requests/sessions_request_spec.rb and spec/requests/users_request_spec.rb are the patterns.
What a component decides about its markup — a label, a placeholder, a class, whether a field renders at all — belongs in spec/components/**/component_spec.rb, not in the request spec for a page that happens to render it. Request specs cover the request: status, redirects, what was saved, what the page is wired to.
A component having no spec yet isn't a reason to put it in the request spec instead. spec/components/pages/register/views/step2/component_spec.rb is the pattern, including the render_x helper that reloads the record so an object updated mid-example isn't answered from the copy the previous render left behind.
No stylesheet loads here, so Capybara calls anything hidden by a class visible. have_no_field/visible: false against a tw:hidden wrapper fails with "found 1 match", which reads as the component rendering the wrong thing. Assert the wrapper instead — page.find("form div.tw\\:hidden input[name='additional']", visible: :all) — and leave real invisibility to a :js system spec.
render_in_view_context takes its subjects as method argumentsA component needing a form_builder renders inside render_in_view_context { form_for … }, which instance_execs its block in the view context — so let values aren't in scope and a bare organization raises NameError. Wrap it in a def rendered_component(organization) and call that from the let(:component); the block closes over the method's locals. spec/components/pages/admin/organizations/form/wrapper/component_spec.rb is the pattern.
display_dev_info? is false in test, so nothing it gates is verifiableControllerHelpers#display_dev_info? opens with !Rails.env.test?. Every only-dev-visible block it wraps is unrendered in the suite, so threading the flag through a component — a wrong default, a missed hop — passes green and is wrong only in development. Reading the call sites isn't enough either — it can't show that a UI::Table cell block is instance_exec'd, so an @ivar in one resolves against the table and is always nil. Check it in the browser signed in as dev@bikeindex.org; the flag needs developer? and MiniProfiler, so the superadmin banner button won't do it.
log_in stubs the auth lookup, so it can't answer whether a session ends — or whether a user is confirmedspec/support/request_spec_helpers.rb's log_in (and every :request_spec_logged_in_as_* context) stubs User.from_auth to return the user, so the cookie is never read and the session outlives anything done to that user — deleting, banning, rotating their auth_token.
The stub also answers User.unconfirmed.from_auth, so unconfirmed_current_user is always present. Sessionable#skip_if_signed_in asks that before it asks whether the user is confirmed, so every request it guards — /session/new, /session/magic_link, /users/new — redirects to please_confirm_email under log_in, whatever the user is.
Either one means signing in for real: include_context :request_spec_signed_in_for_real, which posts real credentials and takes a let(:user) override. spec/requests/sessions_request_spec.rb's "deleted after signing in" and its describe "destroy" are the patterns.
Never open a cassette and change it. Not a URL, not a token, not a timestamp, not an interaction — no matter how small or how obviously right the edit looks. A cassette is a recording of what a real service actually said; an edited one asserts something no service ever returned, and the spec passes against a fiction. This has no exceptions.
The only way a cassette changes is a spec run that records it:
rm the file and run the spec. VCR writes it from scratch, holding exactly the requests the spec makes now. Re-recording without deleting only writes back the requests still being made, so interactions the spec has stopped making survive forever — and because VCR times re_record_interval from the cassette's oldest interaction, one stale entry re-triggers re-recording on every run.spec/rails_helper.rb's filter_sensitive_data and before_record handle them. Add the key there; don't scrub the file by hand.Commit what a run re-records. A modified cassette is a re-recording, not unrelated churn — cassettes carry a re_record_interval and are meant to update. Commit it on the branch you're on, whatever that branch is about. Never git checkout it away to keep a diff focused.
git status after a spec run is the only signal; a run that re-records prints nothing.
Never write WebMock.stub_request. HTTP in a spec is a cassette, with no exceptions. A stub asserts what you imagined a service returns; a cassette records what it actually returned, which is the same reason cassettes are never hand-edited. The handful of WebMock.stub_request calls still in spec/ are legacy — existing usage is not a precedent to copy.
Never partial-mock ENV with allow(ENV).to receive(:[]).and_call_original — it makes every subsequent ENV[...] lookup go through RSpec's message router, which is slow and easy to break by forgetting a .with(...) branch.
Use stub_const against a merged hash instead:
stub_const("ENV", ENV.to_hash.merge("STRIPE_SECRET_KEY" => "sk_test_123"))allow(ENV).to receive(:[]).and_call_original
allow(ENV).to receive(:[]).with("STRIPE_SECRET_KEY").and_return("sk_test_123")Run enqueued jobs by draining them in the default fake mode — SomeJob.drain for one job, Sidekiq::Job.drain_all for everything (clear first with Sidekiq::Job.clear_all when earlier setup left jobs queued). Don't wrap the exercise in Sidekiq::Testing.inline!. Draining lets the request finish and commit before the jobs run, against that committed state — the way production does it — and keeps the test from silently pulling in every cascading job.
Fix every failing test, even ones that were already failing on main. Confirming a failure pre-dates your branch (via git stash or checking out main) explains what broke — not whether you fix it. You fix it.
Reproduce the failure and find out what changed before touching the expectation. Changing an expected value to whatever now renders, loosening eq to include, dropping a count:, or deleting the assertion with a "looks unrelated" handwave all erase signal rather than fix anything. Fix the code if the original assertion was right; update it — with a comment — if the behaviour intentionally changed. The fixing-flaky-failures skill has the same rule for the intermittent case, where it is absolute.
When you're checking several fields on the same object or response, build one expected-attributes hash and assert against it in a single matcher. Don't write a chain of one-attribute-per-line expects.
expect(record).to have_attributes(target_attributes)expect(hash).to eq(target.as_json) for full match, or expect(hash).to include(target_attributes) for partial.This collapses what would be 4 brittle assertions into 1, makes the contract visible at a glance, and gives a single readable diff when something changes. It also avoids the trap of weak per-field assertions like expect(x).to be_present or expect(url).not_to include("blank.png") standing in for "the right value" — match the value directly.
target_attributes = {kind: "found", impounded_description: "Some description"}
expect(impound_record).to have_attributes(target_attributes)
expect(json_result["memberships"]).to eq([target_membership.as_json])expect(impound_record.kind).to eq("found")
expect(impound_record.impounded_description).to be_present
expect(impound_record.impounded_description).to eq("Some description")
logo_url = json_result["memberships"].first["organization_logo_url"]
expect(logo_url).to be_present
expect(logo_url).not_to include("blank.png")
expect(logo_url).to eq(organization.avatar_url)context and letUse context and let to isolate what varies between examples. Each it block should live in a context that names the condition, with let overrides for only what differs in that case. Avoid repeating setup across sibling it blocks.
describe "show_bulk_import?" do
let(:organization) { FactoryBot.build(:organization, pos_kind:) }
let(:pos_kind) { "no_pos" }
it "is falsey" do
expect(organization.show_bulk_import?).to be_falsey
end
context "when ascend" do
let(:pos_kind) { "ascend_pos" }
it "is truthy" do
expect(organization.show_bulk_import?).to be_truthy
end
end
context "when broken_ascend_pos" do
let(:pos_kind) { "broken_ascend_pos" }
it "is truthy" do
expect(organization.show_bulk_import?).to be_truthy
end
end
context "when lightspeed_pos" do
let(:pos_kind) { "lightspeed_pos" }
it "is truthy" do
expect(organization.show_bulk_import?).to be_falsey
end
end
context "when feature show_bulk_import_impound" do
let(:organization) { FactoryBot.build(:organization_with_organization_features, enabled_feature_slugs: ["show_bulk_import_impound"]) }
it "is truthy" do
expect(organization.show_bulk_import?).to be_falsey
end
end
endit "returns truthy for show_bulk_import?" do
organization = FactoryBot.create(:organization, pos_kind: "ascend_pos")
expect(organization.show_bulk_import?).to be_truthy
end
it "returns truthy when feature is included" do
organization = FactoryBot.create(:organization)
allow(organization).to receive(:any_enabled?) { true }
expect(organization.show_bulk_import?).to be_truthy
endThe bad version repeats setup, mocks the object, and doesn't communicate what each case represents.
it blockscontext/let/before isolate what varies. The corollary runs the other way: if two sibling it blocks share the same setup — no differing context, before, or let override between them — collapse them into one example. Each distinct setup earns exactly one it; put all of that setup's assertions (and all of its requests/renders) in that single block.
This is the same instinct as "everything making the same request should be in a single test", generalized: splitting same-setup assertions across sibling it blocks re-runs identical setup (factories, HTTP requests, renders) once per block for zero isolation benefit, and scatters one logical behavior across the file. Two it blocks that differ only in the request params or the assertion — with identical lets and no before between them — are one example.
After writing a spec, scan each context/describe: if it holds multiple it blocks and they don't each sit behind a distinct context/before/let, merge them.
Not when the first request changes what the next one does. Same setup isn't the same starting state once a request has run: signing in consumes session[:return_to], and session[:discourse_redirect] is set with ||= so a second arrival can't replace the first. Both merges pass review and go red. Leave those as sibling contexts with a let for what differs, and say in a comment why they can't share one example — otherwise the next cleanup pass merges them again.
A helper that reads the response has to be a def, not a let. One example making several requests is exactly where a let that parses response.body bites: it memoizes the first response and every later assertion re-reads it, so the example fails while the code is right (or worse, passes while the code is wrong). def re-evaluates.
context "superuser" do
let(:current_user) { FactoryBot.create(:superuser) }
it "offers every view and renders the owner and org-limited perspectives" do
get "#{base_url}/#{bike.id}"
expect(body_text).to match("View as owner of bike")
get "#{base_url}/#{bike.id}", params: {view_as: "owner"}
expect(body_text).to match("Your bike")
get "#{base_url}/#{bike.id}", params: {view_as: "#{org.to_param}.limited"}
expect(body_text).to match("Limited")
end
endcontext "superuser" do
let(:current_user) { FactoryBot.create(:superuser) } # re-created for every it below
it "offers every view" do
get "#{base_url}/#{bike.id}"
expect(body_text).to match("View as owner of bike")
end
it "renders the owner view" do
get "#{base_url}/#{bike.id}", params: {view_as: "owner"}
expect(body_text).to match("Your bike")
end
it "renders an org panel as limited" do
get "#{base_url}/#{bike.id}", params: {view_as: "#{org.to_param}.limited"}
expect(body_text).to match("Limited")
end
end© bikeindex, AGPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .claude/skills/rspec-testing of bikeindex/bike_index.
Open the folder on GitHubat commit ba388d8
Rspec Testing next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Rspec Testing this skillbikeindex/bike_index | 308 | — | ~4.4k | Automated safety check: Pass | AGPL-3.0 | |
| Test Guidelinesgetsentry/sentry-dart | 873 | — | ~3.1k | Automated safety check: Pass | MIT | |
| Characterisation Testscitypaul/.dotfiles | 739 | — | ~3.6k | Automated safety check: Pass | Custom licence | |
| TDD Refactor Guardgustavscirulis/snapgrid | 117 | 1 repos | ~1.8k | Automated safety check: Notes | Custom licence | |
| Test Driven Developmentaiskillstore/marketplace | 430 | — | ~1.9k | Automated safety check: Pass | None | |
| Improvefossasia/eventyay-interpretation | 1.6k | 10 repos | ~3.7k | Automated safety check: Warn | MIT |
getsentry/sentry-dart
Enforce Sentry Dart/Flutter SDK test conventions for naming, structure, and fixtures.
citypaul/.dotfiles
A skill your agent uses when modifying existing code that lacks tests and you need to document its actual current behavior before making changes -- the legacy code dilemma where you need tests to…
gustavscirulis/snapgrid
Pre-refactor safety checklist. An agent skill from gustavscirulis/snapgrid.
aiskillstore/marketplace
Red-green-refactor development methodology requiring verified test coverage.
fossasia/eventyay-interpretation
Survey any codebase as a senior advisor and produce prioritized, self-contained implementation plans for OTHER models/agents to execute.
scott-fryxell/brayness
Write Vitest specs for Vue 3 JavaScript (Vite Plus, happy-dom, @vue/test-utils) and analyze V8 coverage + Fallow health to prioritize test-first refactors.
bikeindex/bike_index
Read live production data from Bike Index through the admin OAuth token — Sidekiq and PgHero status, and the user-submitted bug reports — the same data as the cookie-gated dashboards, but…
bikeindex/bike_index
Embed a local image file into an existing GitHub PR — either in the PR body or as a comment.
bikeindex/bike_index
Add a manufacturer to Bike Index in production through the admin OAuth token (POST /admin/manufacturers).
bikeindex/bike_index
Create or update a pull request for the current branch. An agent skill from bikeindex/bike_index.
bikeindex/bike_index
How to fix a test that fails intermittently in Bike Index — one that passes locally but fails on CI, fails on one shard, passes on re-run, or is already tagged :flaky.
bikeindex/bike_index
Investigate and fix a specific Honeybadger exception in the Bike Index app — pull the fault, read its backtrace, find the offending code, write the fix.
Categories
Bike Index's RSpec testing conventions — how to structure specs with context and let, what kinds of tests to write, and what to avoid (mocks, controller specs, testing private methods). Rspec Testing is an agent skill from bikeindex/bike_index. Bike Index's RSpec testing conventions — how to structure specs with context and let, what kinds of tests to write, and what to avoid (mocks, controller specs, testing private methods).
Rspec Testing fits situations like: modifying any spec.rb file; adding test coverage for new code; refactoring tests; designing the test layout for a new feature.
Run `npx skills add bikeindex/bike_index --skill rspec-testing -a claude-code`. Or copy the skill folder (.claude/skills/rspec-testing in bikeindex/bike_index) into .claude/skills/rspec-testing in your project. Claude Code loads it when a task matches its description.
Run `npx skills add bikeindex/bike_index --skill rspec-testing -a codex`. Or copy the skill folder (.claude/skills/rspec-testing in bikeindex/bike_index) into .agents/skills/rspec-testing in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add bikeindex/bike_index --skill rspec-testing -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/rspec-testing, .gemini/skills/rspec-testing, .github/skills/rspec-testing and .opencode/skills/rspec-testing in your project.
Going by SKILL.md and its folder, Rspec Testing needs the command-line tools its instructions call (git and bundle) and credentials named STRIPE_SECRET_KEY. Our summary lists: A credential in STRIPE_SECRET_KEY.
SKILL.md contains no URLs. Its commands use git, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Rspec Testing is published under the AGPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.4k tokens (SKILL.md is roughly 17k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Rspec Testing: Test Guidelines (getsentry/sentry-dart, 873 stars), Characterisation Tests (citypaul/.dotfiles, 739 stars), TDD Refactor Guard (gustavscirulis/snapgrid, 117 stars) and Test Driven Development (aiskillstore/marketplace, 430 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
bikeindex (a GitHub organization) maintains it in bikeindex/bike_index, which has 308 GitHub stars. The repository holds 12 skills in this directory. The repository was last updated on October 8, 2026.
Source: bikeindex/bike_index on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.