{"id":27243326,"url":"https://github.com/spyna/tdd-workshop","last_synced_at":"2025-10-14T06:15:48.017Z","repository":{"id":259445686,"uuid":"874610787","full_name":"Spyna/tdd-workshop","owner":"Spyna","description":"Code for the Codemotion 2024 workshop on TDD","archived":false,"fork":false,"pushed_at":"2024-10-24T13:04:16.000Z","size":21,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-10T20:42:07.235Z","etag":null,"topics":["kata","tdd","tdd-javascript","tdd-kata","test-driven-development"],"latest_commit_sha":null,"homepage":"https://drive.google.com/file/d/1tMe-jXVAQYaHNJDQDiks3EXThVD4hjXr/view?usp=drive_link","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Spyna.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-18T06:29:12.000Z","updated_at":"2024-11-04T13:27:42.000Z","dependencies_parsed_at":"2024-10-25T13:49:49.108Z","dependency_job_id":"72c80c2d-87a7-4e46-b9d8-5f0ae2487504","html_url":"https://github.com/Spyna/tdd-workshop","commit_stats":null,"previous_names":["spyna/tdd-workshop"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Spyna/tdd-workshop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Spyna%2Ftdd-workshop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Spyna%2Ftdd-workshop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Spyna%2Ftdd-workshop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Spyna%2Ftdd-workshop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Spyna","download_url":"https://codeload.github.com/Spyna/tdd-workshop/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Spyna%2Ftdd-workshop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279018121,"owners_count":26086280,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["kata","tdd","tdd-javascript","tdd-kata","test-driven-development"],"created_at":"2025-04-10T20:27:54.311Z","updated_at":"2025-10-14T06:15:48.000Z","avatar_url":"https://github.com/Spyna.png","language":"JavaScript","readme":"# Codemotion 2024 - TDD workshop\n\n### Slides\n\nhttps://drive.google.com/file/d/1tMe-jXVAQYaHNJDQDiks3EXThVD4hjXr/view?usp=drive_link\n\n### Final Code\n\nThe final code is in the branch `workshop-end`: https://github.com/Spyna/tdd-workshop/tree/workshop-end\n\n### Rules:\n\n1. Add a small test.\n2. Run all the tests and fail.\n3. Make a small change.\n4. Run tests and succeed.\n5. Refactor to remove duplication.\n\n### Introduction\n\nWe want to create a multi-currency report, like this:\n\n| **Supplier** | **Quantity** | **Price** | **Total** |\n| --- | --- | --- | --- |\n| CodeCorp | 1000 | 25 USD | 25000 USD |\n| BugBusters | 400 | 150 CHF | 60000 CHF |\n|  |  |  |  |\n| **Total** |  |  | 65000 USD |\n\nWe also need to specify exchange rates:\n\n| **From** | **To** | **Rate** |\n| --- | --- | --- |\n| CHF | USD | 1.5 |\n\nThinking about what we want to achieve, I wrote some tasks to guide us during development:\n\n- [ ]  generate report\n- [ ]  1000 x 25USD + 400 x 150CHF = 65000USD when the rate is 2:1\n- [ ]  `$5 + 10 CHF = $10` if rate is 2:1\n- [ ]  `$5 * 2 = $10`\n- [ ]  [ ]\n\n### 1 - First step\n\n```jsx\ndescribe(\"Invoice Calculator\", () =\u003e {\n  it(\"should multiply dollars\", () =\u003e {\n    const five = new Dollar(5);\n\n    five.times(2);\n    expect(five.amount).equals(10);\n  });\n});\n```\n\n### 2 - Implementation\n\n```jsx\n\nexport class Dollar {\n  constructor(amount) {\n    this.amount = amount;\n  }\n\n  times(multiplier) {\n    this.amount = 10;\n  }\n}\n\n```\n\n### 3 - The code sucks\n\nWe can now consider the first test completed. Next, we'll deal with those strange side effects.\n\nYes, yes, public fields, side effects, integers for monetary amounts, variable names that don't reflect the value, and so on. Small steps. We’ll note the so-called code smells and move on.\n\n\u003cupdate to-do list\u003e\n\n- [ ]  side effects\n- [ ]  round integer values\n- [ ]  variable names\n\nThe code sucks, let’s improve it.\n\n```jsx\n\nexport class Dollar {\n  constructor(amount) {\n    this.amount = amount;\n  }\n\n  times(multiplier) {\n    this.amount = 5 * 2;\n  }\n}\n\n```\n\nThe TDD cycle consists of three main phases:\n\n- **Write a test**: Imagine how you want the code to work and create the test that reflects this vision, treating it as a story you tell through code.\n- **Make the test pass**: Focus on getting the test to pass quickly. Prioritize speed over perfection, even if it means initially writing imperfect code.\n- **Improve the code**: Once the test passes, clean the code, remove duplications, and improve the structure to follow good clean code practices.\n\nThe goal is \"working clean code that works\"—first ensuring it works, then refining the code to make it clean.\n\nOne way to make it cleaner might be adding another test that fails, then fixing the code.\n\n```jsx\n\nit(\"should multiply dollars\", () =\u003e {\n  const five = new Dollar(5);\n\n  five.times(2);\n  expect(five.amount).to.equals(10);\n\n  five.times(3);\n  expect(five.amount).to.equals(15);\n});\n\n```\n\ncode:\n\n```jsx\n\ntimes(multiplier) {\n  this.amount = 5 * multiplier;\n}\n\n```\n\nAt this point, I think we can refactor the test, trying to improve variable names.\n\n```jsx\n\nlet product = five.times(2);\nexpect(product.amount).equals(10);\n\nproduct = five.times(3);\nexpect(product.amount).equals(15);\n\n```\n\ncode:\n\n```jsx\n\ntimes(multiplier) {\n  return new Dollar(this.amount * multiplier);\n}\n\n```\n\nAdditionally, since `times()` now returns an object, we can assert that `product` (not `product.amount`) equals 10 or 15 dollars.\n\n```jsx\n\nit(\"should multiply dollars\", () =\u003e {\n  const five = new Dollar(5);\n\n  let product = five.times(2);\n  expect(product).to.equals(new Dollar(10));\n\n  product = five.times(3);\n  expect(product).to.equals(new Dollar(15));\n});\n\n```\n\nBut if we run this test, it fails, so we need to modify it using `deep.equals`, as follows:\n\n```jsx\n\nexpect(product).deep.equals(new Dollar(10));\n\n```\n\n**The test passes, the bar stays green. We are happy. Do these steps seem too small?** Remember, TDD is not about taking tiny steps but **being able** to take tiny steps.\n\nDo I write code every day with steps this small? Maybe not.\n\nBut when things get even slightly complicated, I'm glad I can do it. If we can take too-small steps, we can certainly take right-sized steps.\n\nBut if we only take bigger steps, we’ll never know if smaller ones are appropriate.\n\n***\u003cupdate checklist\u003e***\n\nLet’s recap: What have we done so far?\n\n- We listed the smallest tests needed to achieve the final goal.\n- We told a story with a code snippet about how we wanted to handle an operation.\n- We made the test compile using stubs.\n- We ran the test committing horrible sins 😃.\n- Gradually, we generalized the working code, replacing constants with variables.\n- We added items to our to-do list instead of addressing them all at once.\n\n---\n\nI used two main strategies to make the tests pass quickly:\n\n1. **Fake It**: I start by returning a constant and gradually replacing it with variables until reaching the real code.\n2. **Obvious Implementation**: I directly write what I think is the correct implementation.\n\nWhen I use TDD, I often alternate between these strategies. If everything is clear, I use the Obvious Implementation approach. If a test fails unexpectedly, I switch to \"Fake It\" and refactor the code until I feel confident, then return to using Obvious Implementations.\n\n### 5 - Franc-ly Speaking\n\nHow will we approach the first task? The most interesting test on the list? It still seems like too big a step to take.\n\nI'm not sure I can write a test to implement it in just one small step.\n\nA prerequisite seems to be having an object similar to Dollar but representing Francs.\n\nIf we can get the Franc object working the same way as the Dollar object, we will be closer to being able to write and run the mixed addition test.\n\n***\u003cupdate checklist\u003e***\n\n- [ ]  5CHF * 2 = 10 CHF\n\nSo, let's write a test for this:\n\n```jsx\nit(\"should multiply Francs\", () =\u003e {\n  const five = new Franc(5);\n\n  expect(five.times(2).equals(new Franc(10))).to.be.true;\n\n  expect(five.times(3).equals(new Franc(15))).to.be.true;\n});\n```\n\nWhat’s the small step that will get us to a green bar? Copying the `Dollar` code and replacing `Dollar` with `Franc`.\n\n```jsx\n\nexport class Franc {\n  constructor(amount) {\n    this.amount = amount;\n  }\n\n  times(multiplier) {\n    return new Franc(this.amount * multiplier);\n  }\n\n}\n\n```\n\nHold on. Stop for a second. I can feel your thoughts, what you are thinking:\n\nCopying and pasting code? The death of abstraction? The killer of clean design?\n\nDon’t worry, take a deep breath. Relax.\n\nThere. Remember, our cycle has different phases (often going by in mere seconds, but they’re still phases):\n\n- Write a test.\n- Make it compile.\n- Run it to see it fail.\n- Make it pass.\n- Remove duplication.\n\nThe different phases serve different purposes, requiring different solutions and mindsets. The first three phases must pass quickly to reach a stable state.\n\nWe can commit any number of “sins” to get there because speed is more important than design, at least for this brief moment.\n\n---\n\nNow, I'd like to point out something fundamental about TDD: doing TDD doesn’t mean turning off your brain and relying only on the tests.\n\nOn the contrary, we must always stay alert to the code we're writing.\n\nFor example, what happens if I add a test like this:\n\n```jsx\n\nexpect(product).to.deep.equals(new Dollar(15));\n\n```\n\nAre `Francs` equal to `Dollars`? No. So, we need to add a function to compare different types of currency.\n\n```jsx\n\nit(\"should test equality\", () =\u003e {\n  expect(new Franc(5).equals(new Dollar(5))).to.be.false;\n});\n\n```\n\nAnd now we can write the code for the test.\n\n```jsx\n\nequals(other) {\n  return other instanceof Dollar \u0026\u0026 this.amount === other.amount;\n}\n\n```\n\nOkay, I allowed myself to abandon all principles of good design and clean code. And you’ll go tell your friends: “Spinelli says all that stuff about design doesn’t matter.” Stop. The cycle is not complete. **The first four steps of the cycle won’t work without the fifth**. Good design at the right time. **Make it work, then make it right**.\n\n---\n\n### 6 - Equality for All\n\nNow we have plenty of duplication and poor-quality code, and we must remove it before moving on to our next tasks. I’d like to start by generalizing the `equals()` method.\n\n***As in the best cooking shows, I pull out the cake that’s already done from the oven, and here’s a more exhaustive test.***\n\n```jsx\n\nit(\"should test equality\", () =\u003e {\n  expect(new Dollar(5).equals(new Dollar(5))).to.be.true;\n  expect(new Dollar(5).equals(new Dollar(6))).to.be.false;\n\n  expect(new Franc(5).equals(new Franc(5))).to.be.true;\n  expect(new Franc(5).equals(new Franc(6))).to.be.false;\n\n  expect(new Franc(5).equals(new Dollar(6))).to.be.false;\n  expect(new Franc(5).equals(new Dollar(5))).to.be.false;\n});\n\n```\n\nTo do this, we can create a generic `Money` class and make `Dollar` and `Franc` extend it.\n\n```jsx\n\nclass Money {\n  constructor(amount) {\n    this.amount = amount;\n  }\n\n  equals(other) {\n    return (\n      other instanceof Money \u0026\u0026\n      this.amount === other.amount \u0026\u0026\n      this.constructor === other.constructor // Ensure both are of the same type\n    );\n  }\n}\n\n```\n\nHere’s the task list:\n\n- [ ]  Remove unnecessary constructor\n- [ ]  Create a common `times()` method\n\n### 7 - Times We’re Living In\n\nWhat’s on our to-do list that might help us eliminate those annoying, unnecessary subclasses? Maybe we could introduce the concept of currency?\n\nHow do we want to implement currencies right now? Sorry! **I misspoke. Before you scold me, let me rephrase**: How do we want to test currencies right now? Okay, perfect, we’re in TDD.\n\nWe might want to have complex objects representing currencies, with runtime factories to ensure we don’t create more objects than we need. But for now, I think the fastest way (yes! We’re back in the fail-fast, implement-fast phase because we’re starting a new test) is to write a test, and strings will do just fine.\n\n```jsx\n\nit(\"should use the currency\", () =\u003e {\n  expect(\"USD\").equals(Money.dollar(1).currency);\n  expect(\"CHF\").equals(Money.franc(1).currency);\n});\n\n```\n\nThis is “expressing intent”!\n\n```jsx\n\nexport class Money {\n  constructor(amount, currency) {\n    this.amount = amount;\n    this.currency = currency;\n  }\n\n  static dollar(amount) {\n    return new Dollar(amount);\n  }\n\n  static franc(amount) {\n    return new Franc(amount);\n  }\n\n  equals(other) {\n    return (\n      other instanceof Money \u0026\u0026\n      this.amount === other.amount \u0026\u0026\n      this.constructor === other.constructor // Ensure both are of the same type\n    );\n  }\n}\n\nexport class Dollar extends Money {\n  constructor(amount) {\n    super(amount, \"USD\");\n  }\n\n  times(multiplier) {\n    return Money.dollar(this.amount * multiplier);\n  }\n}\n\nexport class Franc extends Money {\n  constructor(amount) {\n    super(amount, \"CHF\");\n  }\n\n  times(multiplier) {\n    return Money.franc(this.amount * multiplier);\n  }\n}\n\n```\n\n**currency equality**\n\n*Add currency to equals: write the test first.*\n\n```jsx\nMoney {\n  equals(\n  - this.constructor === other.constructor\n  + this.currency === other.currency\n}\n```\n\nmodify `times()`:\n\n```jsx\n\ntimes(multiplier) {\n  return new Money(this.amount * multiplier, \"USD\");\n\n}\n\n```\n\nAgain, I feel like I should tell you something about these very small steps. The question is: Am I really recommending working this way? No. But I am recommending that you **be able to** work this way.\n\nI think now we can update our tests.\n\n```jsx\n\nit(\"should test equality\", () =\u003e {\n  expect(Money.dollar(5).equals(Money.dollar(5))).to.be.true;\n  expect(Money.dollar(5).equals(Money.dollar(6))).to.be.false;\n\n  expect(Money.franc(5).equals(Money.franc(5))).to.be.true;\n  expect(Money.franc(5).equals(Money.franc(6))).to.be.false;\n\n  expect(Money.franc(5).equals(Money.dollar(6))).to.be.false;\n  expect(Money.franc(5).equals(Money.dollar(5))).to.be.false;\n});\n\n```\n\nThese are the kinds of adjustments you’ll constantly make with TDD. At this point, I have some considerations:\n\n- Do the tiny steps seem too restrictive? Take bigger steps.\n- Feeling a bit unsure? Take smaller steps.\n\nTDD is a guided process: some in one direction, others in another. There is no right size for the steps, whether in a workshop or the real world.\n\n---\n\nWith this refactor, another thing becomes evident: Now, in the code, I’m only using `Money`, not the `Dollar` or `Franc` classes. And that’s a good thing; I’m hiding the implementation from the outside.\n\nThis is something that came naturally using TDD.\n\nMaybe the test for multiplying `Franc` no longer makes sense. I’ll add it to the task list.\n\n### 8 - Interesting Times\n\nThe implementations of `times()` for both dollars and francs are almost identical.\n\nThere's no obvious way to make them identical. Sometimes, you have to go backward to go forward, a bit like solving a Rubik’s cube.\n\n---\n\nInstead of spending minutes on debatable reasoning, we can simply ask the computer to make the change and run the tests.\n\n**When I teach TDD, I always see this situation: great programmers spending 5-10 minutes reasoning about a question that the computer could answer in 15 seconds.**\n\nWithout tests, you have no choice: you must reason. With tests, you can decide if an experiment would answer the question faster. Sometimes you should just ask the computer.\n\nOkay, at this point, we no longer need the different implementations for `Dollar` and `Franc`. We can unify them into `Money`, using the currency.\n\n```jsx\ntimes(multiplier) {\n  return new Money(this.amount * multiplier, this.currency);\n}\n```\n\n### 9 - The Root of All Evil\n\nThe two subclasses, `Dollar` and `Franc`, now only have their constructors. But since a constructor alone is not a sufficient reason to have a subclass, we want to eliminate the subclasses. We can replace the references to the subclasses with references to the superclass without changing the meaning of the code. Let’s start with `Franc`:\n\n```jsx\n\nstatic dollar(amount) {\n  return new Money(amount, \"USD\");\n}\n\nstatic franc(amount) {\n  return new Money(amount, \"CHF\");\n}\n\n```\n\n### 10 - Sum Money\n\nAlright, remember where we want to go? We want to complete this task:\n\n```jsx\n\n$5 + 10 CHF = $10 if rate is 2:1\n\n```\n\nThe next step that comes to mind is performing currency addition, so the task will become this, and then we'll add another one for: performing the currency exchange.\n\n- [ ]  $5 + $5 = $10\n- [ ]  $5 + 10 CHF = $10 if the rate is 2:1\n\nI think this is simple enough to implement. Let’s write a test!\n\n```jsx\n\nit(\"should sum money\", () =\u003e {\n  expect(\n    Money.dollar(5)\n    .plus(Money.dollar(5))\n    .equals(Money.dollar(10))\n  ).to.be.true;\n});\n\n```\n\nThen implement:\n\n```jsx\n\nplus(money) {\n  return new Money(this.amount + money.amount, this.currency);\n}\n\n```\n\nAt this point, we are at a crucial stage of TDD because we’ve created a test and the minimal implementation to make the test pass. But here’s where the developers' experience is important.\n\n**Can you spot the problem here? \u003csomeone\u003e?**\n\nI call this the ***apples and oranges problem***. What happens if we sum dollars and francs? Let’s write a test about that.\n\n```jsx\n\nexpect(\n  Money.dollar(5)\n  .plus(Money.franc(5))\n  .equals(Money.dollar(10)) // ? what? it doesn't make any sense to me.\n).to.be.true;\n\n```\n\nThis test passes, but it doesn’t make sense to me.\n\nNow we have a few options:\n\n- Fix it and (for example) throw an exception if the currency is different.\n- Create a system to convert the money into different currencies.\n- Activate our developer sense and realize that maybe we're doing something wrong and need another entity, not just `money`, but maybe a bank.\n- Or, simply, since this is something that shouldn't happen, make it impossible.\n\n**Solution**: remove the money from the sum and only use an amount.\n\n```jsx\n\n// test\nexpect(Money.dollar(5).plus(5).equals(Money.dollar(10))).to.be.true;\n\n// class\nplus(amount) {\n  return new Money(this.amount + amount, this.currency);\n}\n\n```\n\nThis is what sometimes happens when using TDD, and in general. We try to take too big a step.\n\nThat is, instead of thinking about the smallest step we can take, we try to reach an abstraction too early, and sometimes this leads us to design errors or complications. But by taking small steps, implementing only what’s strictly necessary, and extending it when needed, we avoid these problems.\n\n### 11 - Change Money\n\n- [ ]  $5 + 10 CHF = $10 if the rate is 2:1\n\nCurrency conversion is not something the money itself is aware of, but rather, we should have a bank.\n\n```jsx\n\nit(\"should change a currency\", () =\u003e {\n  const bank = new Bank();\n  bank.addRate(\"CHF\", \"USD\", 2);\n  bank.addRate(\"USD\", \"CHF\", 0.5);\n\n  const chf = bank.change(Money.dollar(10), \"CHF\");\n  expect(chf.equals(Money.franc(5))).to.be.true;\n\n  const dollars = bank.change(Money.franc(10), \"USD\");\n  expect(dollars.equals(Money.dollar(20))).to.be.true;\n});\n\n```\n\nBank:\n\n```jsx\n\nexport class Bank {\n  rates = [];\n\n  addRate(from, to, rate) {\n    this.rates.push({ from, to, rate });\n  }\n\n  change(money, to) {\n    const rate = this.findRate(money.currency, to);\n    return new Money(money.amount / rate.rate, to);\n  }\n\n  findRate(from, to) {\n    return this.rates.find((rate) =\u003e rate.from === to \u0026\u0026 rate.to === from);\n  }\n}\n\n```\n\nSince we’re running out of time, I invite you to continue from here.\n\n### 12 - Final Report Generation Test\n\nOur goal should be to create a test like this and then implement it.\n\n```jsx\n\ndescribe(\"The bank should exchange money\", () =\u003e {\n  it(\"should generate a report\", () =\u003e {\n    const codeCorp = Money.dollar(25).times(1000);\n\n    const bugBurger = Money.franc(150).times(400);\n\n    const bank = new Bank();\n    bank.addRate(\"CHF\", \"USD\", 1.5);\n\n    const bugBurgerInDollars = bank.change(bugBurger, \"USD\");\n\n    const total = bugBurgerInDollars.plus(codeCorp.amount);\n\n\t\texpect(total.amount).to.equals(65000);\n\n  });\n});\n\n```\n\nSince I don't know all of your email addresses, I will put on LinkedIn the step-by-step schedule I followed with all the steps so you can review it or follow it.\n\n## Recap:\n\nToday, I shared some reflections on Test-Driven Development (TDD) and the importance of design in the software development process.\n\nIn TDD, I follow a cycle made up of five fundamental steps:\n\n1. **Write a test**: Define what you want to test.\n2. **Make it compile**: Ensure the test is syntactically correct.\n3. **Run the test to see it fail**: It’s essential that the test initially fails.\n4. **Make it pass**: Write the minimum code necessary to pass the test.\n5. **Remove duplication**: Clean the code and improve the design.\n\nI use two main strategies:\n\n- **Fake It**: Return a constant and replace it with variables.\n- **Obvious Implementation**: Write what I think is the correct implementation.\n\nI worked to eliminate the `Dollar` and `Franc` classes, replacing them with a generic `Money` class to reduce duplication.\n\n I also tackled the **apples-and-oranges problem**\n\nOur goal was to test currency addition, considering exchange rates, but currency management should fall on a bank, not on `Money`.\n\nClosing… \n\nIn summary, design is crucial in TDD, and we must remain open-minded and flexible during the development cycle.\n\n**Key Points of TDD**: if your boss asks what you learned.\n\n- **Small steps**: TDD is based on taking small, manageable steps.\n- **Refactor regularly**: Always look for opportunities to improve the code once a feature works.\n- **Cycle**: Test, implement, refactor. This keeps the code clean and maintainable.\n\nI think that’s all, thank you very much for your patience, do you have any questions?\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspyna%2Ftdd-workshop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspyna%2Ftdd-workshop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspyna%2Ftdd-workshop/lists"}