{"id":"ea587303-3817-4829-9e31-5439918b5ff2","shortId":"Dd6Pb9","kind":"skill","title":"test-driven-development","tagline":"Strict red-green-refactor TDD workflow for implementing features, fixing bugs, or changing behavior in Rails applications. Enforces the discipline of writing a failing test before any production code. Use whenever you want to implement with TDD — whether a new feature, a bugfix, ","description":"# Test-Driven Development (TDD)\n\n## Overview\n\nWrite the test first. Watch it fail. Write minimal code to pass.\n\n**Core principle:** If you didn't watch the test fail, you don't know if it tests the right thing.\n\n**Violating the letter of the rules is violating the spirit of the rules.**\n\n## The Iron Law\n\n```\nNO PRODUCTION CODE WITHOUT A FAILING TEST FIRST\n```\n\nWrite code before the test? Delete it. Start over.\n\n**No exceptions:**\n\n- Don't keep it as \"reference\"\n- Don't \"adapt\" it while writing tests\n- Don't look at it\n- Delete means delete\n\nImplement fresh from tests. Period.\n\n## Outside-In Development\n\nStart every feature with a high-level test that describes behavior from the user's perspective. Let each failure guide what to build next. Drop to unit tests when you encounter non-trivial logic.\n\nRead `examples/outside-in-testing.md` for a complete walkthrough and `examples/testing-pyramid.md` for how test types combine into an optimal suite.\n\n### The Outer Loop: Feature Specs\n\n1. Take the user story\n2. Write a feature spec describing the behavior end-to-end\n3. Run it — watch it fail\n4. The error tells you what to build next: a route, a controller action, a view, a model method\n5. Build the minimum to get past that error\n6. Run again — next error drives next piece\n7. When you hit non-trivial logic, drop to the inner loop\n\nFeature specs use real database records. No mocks — except for external services (use webmock or fakes). Tests should run without an internet connection.\n\n### The Inner Loop: Unit Tests\n\nWhen the feature spec error points to logic that needs its own proof — a search method, a calculation, a validation rule:\n\n1. Write a unit test for that specific behavior\n2. Follow Red-Green-Refactor (below)\n3. Pass the unit test\n4. Return to the feature spec — next error drives next piece\n\nUnit tests isolate the object under test. Mock collaborators aggressively — the goal is to prove the functionality of this object, not its collaborators. Difficulty testing two objects in isolation signals too-tight coupling.\n\n### When to Drop Down\n\nNot every piece needs a unit test. The feature spec covers the glue.\n\n**Just build it** (feature spec covers it):\n\n- Routes\n- Empty controller actions\n- Simple views and partials\n- Wiring and delegation\n\n**Unit test first** (non-trivial logic):\n\n- Model methods with business logic\n- Service objects\n- Query objects\n- Calculations, validations, transformations\n\nThe testing pyramid: many unit tests at the bottom, few feature tests at the top. Unit tests are fast and precise. Feature tests prove the system works end-to-end. Each plays to its strengths.\n\n## Red-Green-Refactor\n\nThe inner cycle. Every unit test follows this loop — and so does each error-driven step in the outer loop.\n\n```dot\ndigraph tdd_cycle {\n    rankdir=LR;\n    red [label=\"RED\\nWrite failing test\", shape=box, style=filled, fillcolor=\"#ffcccc\"];\n    verify_red [label=\"Verify fails\\ncorrectly\", shape=diamond];\n    green [label=\"GREEN\\nMinimal code\", shape=box, style=filled, fillcolor=\"#ccffcc\"];\n    verify_green [label=\"Verify passes\\nAll green\", shape=diamond];\n    refactor [label=\"REFACTOR\\nClean up\", shape=box, style=filled, fillcolor=\"#ccccff\"];\n    next [label=\"Next\", shape=ellipse];\n\n    red -> verify_red;\n    verify_red -> green [label=\"yes\"];\n    verify_red -> red [label=\"wrong\\nfailure\"];\n    green -> verify_green;\n    verify_green -> refactor [label=\"yes\"];\n    verify_green -> green [label=\"no\"];\n    refactor -> verify_green [label=\"stay\\ngreen\"];\n    verify_green -> next;\n    next -> red;\n}\n```\n\n### RED — Write Failing Test\n\nWrite one minimal test showing what should happen.\n\n<Good>\n\n```ruby\nRSpec.describe Item, \".search\" do\n  it \"filters items by the search term\" do\n    desired_item = create(:item, name: \"Widget\")\n    _other_item = create(:item, name: \"Gadget\")\n\n    expect(Item.search(\"Widget\")).to eq [desired_item]\n  end\nend\n```\n\nClear name, tests real behavior, one thing.\n\n</Good>\n\n<Bad>\n\n```ruby\nit \"search works\" do\n  relation = spy(\"relation\")\n  allow(Item).to receive(:where).and_return(relation)\n\n  Item.search(\"Widget\")\n\n  expect(Item).to have_received(:where).with(name: \"Widget\")\nend\n```\n\nVague name, tests spy interactions not real behavior, proves nothing about whether search actually returns the right items.\n\n</Bad>\n\n**Requirements:**\n\n- One behavior\n- Clear name\n- Feature specs: real records, no mocks (except external services)\n- Unit tests: mock collaborators, test the object in isolation\n\n### Verify RED — Watch It Fail\n\n**MANDATORY. Never skip.**\n\n```bash\nbundle exec rspec spec/models/item_spec.rb\n```\n\nConfirm:\n\n- Test fails (not errors)\n- Failure message is expected\n- Fails because feature missing (not typos)\n\n**Test passes?** You're testing existing behavior. Fix test.\n\n**Test errors?** Fix error, re-run until it fails correctly.\n\n### GREEN — Minimal Code\n\nWrite simplest code to pass the test.\n\n<Good>\n\n```ruby\nclass Item < ApplicationRecord\n  def self.search(term)\n    where(name: term)\n  end\nend\n```\n\nJust enough to pass.\n\n</Good>\n\n<Bad>\n\n```ruby\nclass Item < ApplicationRecord\n  def self.search(term, fuzzy: false, limit: nil, scope: :all)\n    # YAGNI — the test asked for name filtering, not a search framework\n  end\nend\n```\n\nOver-engineered.\n\n</Bad>\n\nDon't add features, refactor other code, or \"improve\" beyond the test.\n\n### Verify GREEN — Watch It Pass\n\n**MANDATORY.**\n\n```bash\nbundle exec rspec spec/models/item_spec.rb\n```\n\nConfirm:\n\n- Test passes\n- Other tests still pass\n- Output pristine (no errors, warnings)\n\n**Test fails?** Fix code, not test.\n\n**Other tests fail?** Fix now.\n\n### REFACTOR — Clean Up\n\nAfter green only:\n\n- Remove duplication\n- Improve names\n- Extract helpers\n\nKeep tests green. Don't add behavior.\n\n### Repeat\n\nReturn to the feature spec. Next error drives the next piece. Drop to unit tests when needed. Continue until the feature spec is green.\n\n## Good Tests\n\n| Quality          | Good                                | Bad                                              |\n| ---------------- | ----------------------------------- | ------------------------------------------------ |\n| **Minimal**      | One thing. \"and\" in name? Split it. | `it \"validates email and domain and whitespace\"` |\n| **Clear**        | Name describes behavior             | `it \"test1\"`                                     |\n| **Shows intent** | Demonstrates desired API            | Obscures what code should do                     |\n\n## Why Order Matters\n\n**\"I'll write tests after to verify it works\"**\n\nTests written after code pass immediately. Passing immediately proves nothing:\n\n- Might test wrong thing\n- Might test implementation, not behavior\n- Might miss edge cases you forgot\n- You never saw it catch the bug\n\nTest-first forces you to see the test fail, proving it actually tests something.\n\n**\"I already manually tested all the edge cases\"**\n\nManual testing is ad-hoc. You think you tested everything but:\n\n- No record of what you tested\n- Can't re-run when code changes\n- Easy to forget cases under pressure\n- \"It worked when I tried it\" ≠ comprehensive\n\nAutomated tests are systematic. They run the same way every time.\n\n**\"Deleting X hours of work is wasteful\"**\n\nSunk cost fallacy. The time is already gone. Your choice now:\n\n- Delete and rewrite with TDD (X more hours, high confidence)\n- Keep it and add tests after (30 min, low confidence, likely bugs)\n\nThe \"waste\" is keeping code you can't trust. Working code without real tests is technical debt.\n\n**\"TDD is dogmatic, being pragmatic means adapting\"**\n\nTDD IS pragmatic:\n\n- Finds bugs before commit (faster than debugging after)\n- Prevents regressions (tests catch breaks immediately)\n- Documents behavior (tests show how to use code)\n- Enables refactoring (change freely, tests catch breaks)\n\n\"Pragmatic\" shortcuts = debugging in production = slower.\n\n**\"Tests after achieve the same goals — it's spirit not ritual\"**\n\nNo. Tests-after answer \"What does this do?\" Tests-first answer \"What should this do?\"\n\nTests-after are biased by your implementation. You test what you built, not what's required. You verify remembered edge cases, not discovered ones.\n\nTests-first force edge case discovery before implementing. Tests-after verify you remembered everything (you didn't).\n\n30 minutes of tests after ≠ TDD. You get coverage, lose proof tests work.\n\n## Common Rationalizations\n\n| Excuse                                 | Reality                                                                 |\n| -------------------------------------- | ----------------------------------------------------------------------- |\n| \"Too simple to test\"                   | Simple code breaks. Test takes 30 seconds.                              |\n| \"I'll test after\"                      | Tests passing immediately prove nothing.                                |\n| \"Tests after achieve same goals\"       | Tests-after = \"what does this do?\" Tests-first = \"what should this do?\" |\n| \"Already manually tested\"              | Ad-hoc ≠ systematic. No record, can't re-run.                           |\n| \"Deleting X hours is wasteful\"         | Sunk cost fallacy. Keeping unverified code is technical debt.           |\n| \"Keep as reference, write tests first\" | You'll adapt it. That's testing after. Delete means delete.             |\n| \"Need to explore first\"                | Fine. Throw away exploration, start with TDD.                           |\n| \"Test hard = design unclear\"           | Listen to test. Hard to test = hard to use.                             |\n| \"TDD will slow me down\"                | TDD faster than debugging. Pragmatic = test-first.                      |\n| \"Manual test faster\"                   | Manual doesn't prove edge cases. You'll re-test every change.           |\n| \"Existing code has no tests\"           | You're improving it. Add tests for existing code.                       |\n\n## Red Flags — STOP and Start Over\n\n- Code before test\n- Test after implementation\n- Test passes immediately\n- Can't explain why test failed\n- Tests added \"later\"\n- Rationalizing \"just this once\"\n- \"I already manually tested it\"\n- \"Tests after achieve the same purpose\"\n- \"It's about spirit not ritual\"\n- \"Keep as reference\" or \"adapt existing code\"\n- \"Already spent X hours, deleting is wasteful\"\n- \"TDD is dogmatic, I'm being pragmatic\"\n- \"This is different because...\"\n\n**All of these mean: Delete code. Start over with TDD.**\n\n## Example: Feature (Outside-In)\n\n**Story:** As a guest, I can search for items so I can find what I want.\n\n**Outer loop — Feature spec**\n\n```ruby\n# spec/features/guest_searches_for_items_spec.rb\nfeature \"Guest searches for items\" do\n  scenario \"by name\" do\n    create(:item, name: \"Widget\")\n\n    visit root_path\n    fill_in \"Search\", with: \"Widget\"\n    click_on \"Search\"\n\n    expect(page).to have_content(\"Widget\")\n  end\nend\n```\n\nRun it. First error: no route. Add the route. Next error: no controller. Create it. Next error: no `search` method on `Item` — drop to the inner loop.\n\n**Inner loop — Unit test**\n\n```ruby\n# spec/models/item_spec.rb\nRSpec.describe Item, \".search\" do\n  it \"filters items by name\" do\n    desired = create(:item, name: \"Widget\")\n    _other = create(:item, name: \"Gadget\")\n\n    expect(Item.search(\"Widget\")).to eq [desired]\n  end\nend\n```\n\nVerify RED. Implement `Item.search`. Verify GREEN. Return to feature spec. Next error drives next piece. Continue until the feature spec is green.\n\n## Example: Bug Fix\n\n**Bug:** Empty email accepted\n\nStart with a feature spec reproducing the bug from the user's perspective, then drop to a unit test for the validation logic.\n\n**Outer loop — Feature spec**\n\n```ruby\n# spec/features/guest_registers_spec.rb\nfeature \"Guest registers\" do\n  scenario \"with blank email\" do\n    visit new_registration_path\n    fill_in \"Email\", with: \"\"\n    click_on \"Register\"\n\n    expect(page).to have_content(\"Email can't be blank\")\n  end\nend\n```\n\n**Inner loop — Unit test**\n\n```ruby\n# spec/models/user_spec.rb\nRSpec.describe User do\n  it \"rejects empty email\" do\n    user = User.new(email: \"\")\n\n    expect(user).not_to be_valid\n    expect(user.errors[:email]).to include(\"can't be blank\")\n  end\nend\n```\n\n**GREEN**\n\n```ruby\nclass User < ApplicationRecord\n  validates :email, presence: true\nend\n```\n\nUnit test passes. Return to feature spec. Continue until green.\n\n## Verification Checklist\n\nBefore marking work complete:\n\n- [ ] Every new function/method has a test\n- [ ] Watched each test fail before implementing\n- [ ] Each test failed for expected reason (feature missing, not typo)\n- [ ] Wrote minimal code to pass each test\n- [ ] All tests pass\n- [ ] Output pristine (no errors, warnings)\n- [ ] Tests use real code (mocks only if unavoidable)\n- [ ] Edge cases and errors covered\n\nCan't check all boxes? You skipped TDD. Start over.\n\n## When Stuck\n\n| Problem                | Solution                                                             |\n| ---------------------- | -------------------------------------------------------------------- |\n| Don't know how to test | Write wished-for API. Write assertion first. Ask your human partner. |\n| Test too complicated   | Design too complicated. Simplify interface.                          |\n| Must mock everything   | Code too coupled. Use dependency injection.                          |\n| Test setup huge        | Extract helpers. Still complex? Simplify design.                     |\n\n## Debugging Integration\n\nBug found? Write failing test reproducing it. Follow TDD cycle. Test proves fix and prevents regression.\n\nNever fix bugs without a test.\n\n## Testing Anti-Patterns\n\nWhen adding mocks or test utilities, read `references/testing-anti-patterns.md` to avoid common pitfalls:\n\n- Testing mock behavior instead of real behavior\n- Adding test-only methods to production classes\n- Mocking without understanding dependencies\n\n## Final Rule\n\n```\nProduction code → test exists and failed first\nOtherwise → not TDD\n```\n\nNo exceptions without your human partner's permission.","tags":["test","driven","development","rails","consultant","thoughtbot","agent-skills","claude-code-consulting","claude-code-plugin","claude-code-rails","claude-code-skill","claude-code-skills"],"capabilities":["skill","source-thoughtbot","skill-test-driven-development","topic-agent-skills","topic-claude-code-consulting","topic-claude-code-plugin","topic-claude-code-rails","topic-claude-code-skill","topic-claude-code-skills","topic-claude-skills"],"categories":["rails-consultant"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/thoughtbot/rails-consultant/test-driven-development","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add thoughtbot/rails-consultant","source_repo":"https://github.com/thoughtbot/rails-consultant","install_from":"skills.sh"}},"qualityScore":"0.454","qualityRationale":"deterministic score 0.45 from registry signals: · indexed on github topic:agent-skills · 9 github stars · SKILL.md body (13,996 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:08:43.481Z","embedding":null,"createdAt":"2026-05-18T13:14:15.944Z","updatedAt":"2026-05-18T19:08:43.481Z","lastSeenAt":"2026-05-18T19:08:43.481Z","tsv":"'1':210,331 '2':215,340 '3':227,347 '30':1114,1254,1280 '4':233,352 '5':252 '6':261 '7':269 'accept':1651 'achiev':1184,1293,1457 'action':246,424 'actual':706,1019 'ad':1034,1314,1444,1910,1928 'ad-hoc':1033,1313 'adapt':130,1143,1346,1471 'add':839,900,1111,1417,1568 'aggress':372 'allow':673 'alreadi':1023,1093,1310,1451,1474 'answer':1197,1205 'anti':1907 'anti-pattern':1906 'api':957,1847 'applic':22 'applicationrecord':795,811,1751 'ask':824,1851 'assert':1849 'autom':1069 'avoid':1918 'away':1361 'bad':931 'bash':742,855 'behavior':19,163,222,339,662,700,713,768,901,950,993,1162,1923,1927 'beyond':846 'bias':1214 'blank':1687,1710,1744 'bottom':459 'box':525,544,564,1827 'break':1159,1175,1277 'bug':16,1006,1119,1148,1646,1648,1659,1883,1901 'bugfix':48 'build':175,240,253,415 'built':1222 'bundl':743,856 'busi':442 'calcul':327,448 'case':997,1029,1059,1231,1240,1400,1819 'catch':1004,1158,1174 'ccccff':568 'ccffcc':548 'chang':18,1055,1171,1407 'check':1825 'checklist':1768 'choic':1096 'class':793,809,1749,1935 'clean':884 'clear':658,714,947 'click':1551,1698 'code':34,64,105,112,542,784,787,843,875,960,978,1054,1124,1130,1168,1276,1334,1409,1421,1428,1473,1497,1797,1813,1866,1943 'collabor':371,385,728 'combin':200 'commit':1150 'common':1267,1919 'complet':192,1772 'complex':1878 'complic':1857,1860 'comprehens':1068 'confid':1107,1117 'confirm':747,860 'connect':304 'content':1558,1705 'continu':920,1638,1764 'control':245,423,1574 'core':67 'correct':781 'cost':1088,1330 'coupl':396,1868 'cover':411,419,1822 'coverag':1262 'creat':639,645,1539,1575,1606,1611 'cycl':493,515,1892 'databas':286 'debt':1136,1337 'debug':1153,1178,1387,1881 'def':796,812 'deleg':431 'delet':116,140,142,1080,1098,1324,1352,1354,1478,1496 'demonstr':955 'depend':1870,1939 'describ':162,220,949 'design':1368,1858,1880 'desir':637,654,956,1605,1620 'develop':4,52,151 'diamond':537,557 'didn':71,1252 'differ':1490 'difficulti':386 'digraph':513 'disciplin':25 'discov':1233 'discoveri':1241 'document':1161 'doesn':1396 'dogmat':1139,1483 'domain':944 'dot':512 'drive':266,360,910,1635 'driven':3,51,506 'drop':177,277,399,914,1584,1666 'duplic':890 'easi':1056 'edg':996,1028,1230,1239,1399,1818 'ellips':573 'email':942,1650,1688,1696,1706,1725,1729,1738,1753 'empti':422,1649,1724 'enabl':1169 'encount':183 'end':224,226,479,481,656,657,692,802,803,832,833,1560,1561,1621,1622,1711,1712,1745,1746,1756 'end-to-end':223,478 'enforc':23 'engin':836 'enough':805 'eq':653,1619 'error':235,260,265,314,359,505,751,772,774,870,909,1565,1572,1578,1634,1808,1821 'error-driven':504 'everi':153,402,494,1078,1406,1773 'everyth':1040,1250,1865 'exampl':1502,1645 'examples/outside-in-testing.md':189 'examples/testing-pyramid.md':195 'except':121,290,722,1953 'excus':1269 'exec':744,857 'exist':767,1408,1420,1472,1945 'expect':649,683,755,1554,1615,1701,1730,1736,1789 'explain':1439 'explor':1357,1362 'extern':292,723 'extract':893,1875 'fail':29,61,76,108,232,522,534,614,738,749,756,780,873,880,1016,1442,1782,1787,1886,1947 'failur':171,752 'fake':297 'fallaci':1089,1331 'fals':816 'fast':469 'faster':1151,1385,1394 'featur':14,46,154,208,218,282,312,356,409,417,461,472,716,758,840,906,923,1503,1525,1529,1631,1641,1655,1677,1681,1762,1791 'ffcccc':529 'fill':527,546,566,1546,1694 'fillcolor':528,547,567 'filter':630,827,1600 'final':1940 'find':1147,1519 'fine':1359 'first':58,110,434,1009,1204,1237,1305,1343,1358,1391,1564,1850,1948 'fix':15,769,773,874,881,1647,1895,1900 'flag':1423 'follow':341,497,1890 'forc':1010,1238 'forget':1058 'forgot':999 'found':1884 'framework':831 'freeli':1172 'fresh':144 'function':379 'function/method':1775 'fuzzi':815 'gadget':648,1614 'get':257,1261 'glue':413 'goal':374,1187,1295 'gone':1094 'good':927,930 'green':8,344,489,538,540,550,555,579,588,590,592,597,598,603,608,782,850,887,897,926,1628,1644,1747,1766 'guest':1510,1530,1682 'guid':172 'happen':623 'hard':1367,1373,1376 'helper':894,1876 'high':158,1106 'high-level':157 'hit':272 'hoc':1035,1315 'hour':1082,1105,1326,1477 'huge':1874 'human':1853,1956 'immedi':980,982,1160,1288,1436 'implement':13,40,143,991,1217,1243,1433,1625,1784 'improv':845,891,1415 'includ':1740 'inject':1871 'inner':280,306,492,1587,1589,1713 'instead':1924 'integr':1882 'intent':954 'interact':697 'interfac':1862 'internet':303 'iron':101 'isol':365,391,733 'item':626,631,638,640,644,646,655,674,684,710,794,810,1515,1533,1540,1583,1596,1601,1607,1612 'item.search':650,681,1616,1626 'keep':124,895,1108,1123,1332,1338,1467 'know':80,1839 'label':519,532,539,551,559,570,580,585,594,599,604 'later':1445 'law':102 'let':169 'letter':89 'level':159 'like':1118 'limit':817 'listen':1370 'll':967,1283,1345,1402 'logic':187,276,317,438,443,1674 'look':137 'loop':207,281,307,499,511,1524,1588,1590,1676,1714 'lose':1263 'low':1116 'lr':517 'm':1485 'mandatori':739,854 'mani':454 'manual':1024,1030,1311,1392,1395,1452 'mark':1770 'matter':965 'mean':141,1142,1353,1495 'messag':753 'method':251,325,440,1581,1932 'might':985,989,994 'min':1115 'minim':63,618,783,932,1796 'minimum':255 'minut':1255 'miss':759,995,1792 'mock':289,370,721,727,1814,1864,1911,1922,1936 'model':250,439 'must':1863 'nall':554 'name':641,647,659,690,694,715,800,826,892,937,948,1537,1541,1603,1608,1613 'nclean':561 'ncorrect':535 'need':319,404,919,1355 'never':740,1001,1899 'new':45,1691,1774 'next':176,241,264,267,358,361,569,571,609,610,908,912,1571,1577,1633,1636 'nfailur':587 'ngreen':606 'nil':818 'nminim':541 'non':185,274,436 'non-trivi':184,273,435 'noth':702,984,1290 'nwrite':521 'object':367,382,389,445,447,731 'obscur':958 'one':617,663,712,933,1234 'optim':203 'order':964 'otherwis':1949 'outer':206,510,1523,1675 'output':867,1805 'outsid':149,1505 'outside-in':148,1504 'over-engin':834 'overview':54 'page':1555,1702 'partial':428 'partner':1854,1957 'pass':66,348,553,763,789,807,853,862,866,979,981,1287,1435,1759,1799,1804 'past':258 'path':1545,1693 'pattern':1908 'period':147 'permiss':1959 'perspect':168,1664 'piec':268,362,403,913,1637 'pitfal':1920 'play':483 'point':315 'pragmat':1141,1146,1176,1388,1487 'precis':471 'presenc':1754 'pressur':1061 'prevent':1155,1897 'principl':68 'pristin':868,1806 'problem':1835 'product':33,104,1180,1934,1942 'proof':322,1264 'prove':377,474,701,983,1017,1289,1398,1894 'purpos':1460 'pyramid':453 'qualiti':929 'queri':446 'rail':21 'rankdir':516 'ration':1268,1446 're':765,776,1051,1322,1404,1414 're-run':775,1050,1321 're-test':1403 'read':188,1915 'real':285,661,699,718,1132,1812,1926 'realiti':1270 'reason':1790 'receiv':676,687 'record':287,719,1043,1318 'red':7,343,488,518,520,531,574,576,578,583,584,611,612,735,1422,1624 'red-green-refactor':6,342,487 'refactor':9,345,490,558,560,593,601,841,883,1170 'refer':127,1340,1469 'references/testing-anti-patterns.md':1916 'regist':1683,1700 'registr':1692 'regress':1156,1898 'reject':1723 'relat':670,672,680 'rememb':1229,1249 'remov':889 'repeat':902 'reproduc':1657,1888 'requir':711,1226 'return':353,679,707,903,1629,1760 'rewrit':1100 'right':85,709 'ritual':1192,1466 'root':1544 'rout':243,421,1567,1570 'rspec':745,858 'rspec.describe':625,1595,1719 'rubi':624,665,792,808,1527,1593,1679,1717,1748 'rule':92,99,330,1941 'run':228,262,300,777,1052,1074,1323,1562 'saw':1002 'scenario':1535,1685 'scope':819 'search':324,627,634,667,705,830,1513,1531,1548,1553,1580,1597 'second':1281 'see':1013 'self.search':797,813 'servic':293,444,724 'setup':1873 'shape':524,536,543,556,563,572 'shortcut':1177 'show':620,953,1164 'signal':392 'simpl':425,1272,1275 'simplest':786 'simplifi':1861,1879 'skill' 'skill-test-driven-development' 'skip':741,1829 'slow':1381 'slower':1181 'solut':1836 'someth':1021 'source-thoughtbot' 'spec':209,219,283,313,357,410,418,717,907,924,1526,1632,1642,1656,1678,1763 'spec/features/guest_registers_spec.rb':1680 'spec/features/guest_searches_for_items_spec.rb':1528 'spec/models/item_spec.rb':746,859,1594 'spec/models/user_spec.rb':1718 'specif':338 'spent':1475 'spi':671,696 'spirit':96,1190,1464 'split':938 'start':118,152,1363,1426,1498,1652,1831 'stay':605 'step':507 'still':865,1877 'stop':1424 'stori':214,1507 'strength':486 'strict':5 'stuck':1834 'style':526,545,565 'suit':204 'sunk':1087,1329 'system':476 'systemat':1072,1316 'take':211,1279 'tdd':10,42,53,514,1102,1137,1144,1259,1365,1379,1384,1481,1501,1830,1891,1951 'technic':1135,1336 'tell':236 'term':635,798,801,814 'test':2,30,50,57,75,83,109,115,134,146,160,180,198,298,309,335,351,364,369,387,407,433,452,456,462,467,473,496,523,615,619,660,695,726,729,748,762,766,770,771,791,823,848,861,864,872,877,879,896,917,928,969,975,986,990,1008,1015,1020,1025,1031,1039,1047,1070,1112,1133,1157,1163,1173,1182,1195,1203,1211,1219,1236,1245,1257,1265,1274,1278,1284,1286,1291,1297,1304,1312,1342,1350,1366,1372,1375,1390,1393,1405,1412,1418,1430,1431,1434,1441,1443,1453,1455,1592,1670,1716,1758,1778,1781,1786,1801,1803,1810,1842,1855,1872,1887,1893,1904,1905,1913,1921,1930,1944 'test-driven':49 'test-driven-develop':1 'test-first':1007,1389 'test-on':1929 'test1':952 'tests-aft':1194,1210,1244,1296 'tests-first':1202,1235,1303 'thing':86,664,934,988 'think':1037 'throw':1360 'tight':395 'time':1079,1091 'too-tight':393 'top':465 'topic-agent-skills' 'topic-claude-code-consulting' 'topic-claude-code-plugin' 'topic-claude-code-rails' 'topic-claude-code-skill' 'topic-claude-code-skills' 'topic-claude-skills' 'transform':450 'tri':1066 'trivial':186,275,437 'true':1755 'trust':1128 'two':388 'type':199 'typo':761,1794 'unavoid':1817 'unclear':1369 'understand':1938 'unit':179,308,334,350,363,406,432,455,466,495,725,916,1591,1669,1715,1757 'unverifi':1333 'use':35,284,294,1167,1378,1811,1869 'user':166,213,1662,1720,1727,1731,1750 'user.errors':1737 'user.new':1728 'util':1914 'vagu':693 'valid':329,449,941,1673,1735,1752 'verif':1767 'verifi':530,533,549,552,575,577,582,589,591,596,602,607,734,849,972,1228,1247,1623,1627 'view':248,426 'violat':87,94 'visit':1543,1690 'walkthrough':193 'want':38,1522 'warn':871,1809 'wast':1086,1121,1328,1480 'watch':59,73,230,736,851,1779 'way':1077 'webmock':295 'whenev':36 'whether':43,704 'whitespac':946 'widget':642,651,682,691,1542,1550,1559,1609,1617 'wire':429 'wish':1845 'wished-for':1844 'without':106,301,1131,1902,1937,1954 'work':477,668,974,1063,1084,1129,1266,1771 'workflow':11 'write':27,55,62,111,133,216,332,613,616,785,968,1341,1843,1848,1885 'written':976 'wrong':586,987 'wrote':1795 'x':1081,1103,1325,1476 'yagni':821 'yes':581,595","prices":[{"id":"92b31662-9e8c-4520-8e80-883aabad9fec","listingId":"ea587303-3817-4829-9e31-5439918b5ff2","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"thoughtbot","category":"rails-consultant","install_from":"skills.sh"},"createdAt":"2026-05-18T13:14:15.944Z"}],"sources":[{"listingId":"ea587303-3817-4829-9e31-5439918b5ff2","source":"github","sourceId":"thoughtbot/rails-consultant/test-driven-development","sourceUrl":"https://github.com/thoughtbot/rails-consultant/tree/main/skills/test-driven-development","isPrimary":false,"firstSeenAt":"2026-05-18T13:14:15.944Z","lastSeenAt":"2026-05-18T19:08:43.481Z"}],"details":{"listingId":"ea587303-3817-4829-9e31-5439918b5ff2","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"thoughtbot","slug":"test-driven-development","github":{"repo":"thoughtbot/rails-consultant","stars":9,"topics":["agent-skills","claude-code-consulting","claude-code-plugin","claude-code-rails","claude-code-skill","claude-code-skills","claude-skills"],"license":"mit","html_url":"https://github.com/thoughtbot/rails-consultant","pushed_at":"2026-03-27T16:24:47Z","description":"A collection of skills for Rails development and consulting with an emphasis on learning, communication, and client success. ","skill_md_sha":"6a9f2275d9468726ca5bd8422f13b1600303638e","skill_md_path":"skills/test-driven-development/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/thoughtbot/rails-consultant/tree/main/skills/test-driven-development"},"layout":"multi","source":"github","category":"rails-consultant","frontmatter":{"name":"test-driven-development","description":"Strict red-green-refactor TDD workflow for implementing features, fixing bugs, or changing behavior in Rails applications. Enforces the discipline of writing a failing test before any production code. Use whenever you want to implement with TDD — whether a new feature, a bugfix, a refactor, or any behavior change."},"skills_sh_url":"https://skills.sh/thoughtbot/rails-consultant/test-driven-development"},"updatedAt":"2026-05-18T19:08:43.481Z"}}