Avoid PTSD use TDD
Tags: testing, ruby, programming
Published: 2024-06-12 13:18:28

## Introduction 👋 
> Test-Driven Development (TDD) is an approach that focuses on writing tests before writing the actual code. The general aim is to never write new functionality without a failing test first.
> 
>In basic principle remember the following...
>**RED-GREEN-REFACTOR**
>

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1rh222odoogp1nuaoh4e.png)

### Why TDD?
#### Occasionally you may see the following smells
- Tests that don't make sense.
	- Poor use of testing practices like non descriptive context and it blocks lead to tests that are *hard to read*, which makes it very difficult to debug and maintain
- Sometimes a feature is not understood correctly, and edge cases are not addressed
	- If you don't understand the spec how can you write it effectively?
	- If you don't agree with the spec / ticket its best to raise it ASAP, *don't be afraid to speak out*!
	- The same goes for working on bugs, if you have a very coupled system, ensure the fix proposed makes sense, but first *understand the feature*.
- Not testing the negative enough
	- You should never assume that things are always going to go right (*cough *cough [Murphy's Law](https://en.wikipedia.org/wiki/Murphy%27s_law#:~:text=Murphy's%20law%20is%20an%20adage,at%20the%20worst%20possible%20time.%22)), we should be trying to catch possible errors and then write tests for it. 
- The develop is not envisaging the properly. IE pretend to be the user.
	- Sometimes its good to stop and think if what your are building / testing could use more enhancements or be friendlier to the user
	- This isn't always possible but sometimes things are missed in designs or tickets you might find them yourself.
-  Bloated tests
	- Be careful not to over assert functionality, test what is key to the feature, make it simple and move on. Don't test things that your language does, test things the feature does.

>*Poorly written tests that over-specify the functionality of objects make systems harder to change, leading to frustration with the practice of testing and TDD itself.*
>
>*If an organization or project wants to see the value of testing in the long term, they need to ensure that test suites are kept lean, that tests are structured to aid developers in understanding them, and that failures provide useful feedback.*
>*[Source](https://jardo.dev/dont-assert-return-types-tdd)

#### Thankfully TDD can help!

### The Basic formula 🧑‍🍳 

- **Write Test:**  ✏️ 
    Start by writing a test that defines the desired behaviour of a particular piece of code. This test should initially fail because the code to fulfil the behaviour hasn’t been implemented yet.
- **Run it, for Red:** 🔴 
	Run your test, knowing that its going to fail. It's okay to fail 😜 
- **Write Code:**  ✏️ 
    Write the *minimum amount of code* necessary to make the test pass. In this pass we just want to make the thing work, it doesn't matter if its ugly.
- **Run it for Green:** 🟢 
	Run your test again, it should pass. If it doesn't pass, go back a step and keep working to get the green.
- **Refactor:**  ✏️ 
    After the test passes, refactor the code to improve its quality, maintainability, and efficiency. It's good here to look at OO principles and best practices often used in ruby. (In particular when building new classes or PORO's)
- **Run it for Green:** 🟢 
	Run your test again, ensuring it passes or go back to refactoring
- **Repeat on next item:** 🔁 
	Move onto the next piece of work and start again

> Handy tips! 
>- As a first step I like to write up a skeleton of tests. So for instance I would copy the ticket and then draft up a skeleton of the context blocks and it blocks before hand.
> - Also break things down into segments and pieces of logic. Smaller bits are easier to manage.
> - Don't be afraid to use an AI when refactoring! I do it all the time its super handy, just make sure you ask the write questions and always take suggestions with a grain of salt. Sometimes it comes up with garbage but often theres excellent feedback. Write down what you learn use it next time!

### Use AAA to supercharge your tests

"Arrange, Act, Assert" (AAA) is a pattern for arranging and formatting code in unit tests. The idea is to divide your test method into three sections, which are separated by blank lines:

1. **Arrange**: This is where you set up the conditions for your test. This could involve creating objects, initialising data, setting up mocks or stubs, etc.
    
2. **Act**: This is where you perform the action that you're testing. Usually, this is a single function call.
    
3. **Assert**: This is where you check that the action you performed in the "Act" step produced the expected result.

Check out the following ruby example using RPSEC
```Ruby
RSpec.describe Calculator do
  describe '#add' do
    it 'adds two numbers' do
      # Arrange
      calculator = Calculator.new

      # Act
      result = calculator.add(2, 3)

      # Assert
      expect(result).to eq(5)
    end
  end
end
```

### Benefits of TDD ✅ 
- Forces developer to think deeper about the requirements and design of the spec
- Catch potential bugs early. 🐞 
- Catch design flaws early, leading to higher code / feature quality.
- Debugging becomes faster and more efficient.
- Comprehensive tests makes refactoring safer ⛑️ 

### Tidbit about BDD
> BDD or Behaviour Driven Development, extends on Test driven development by writing test cases in a way anyone can understand.

- It focuses on the behaviour of software from the user’s perspective.
-  It's an *agile software development technique*, in that it *encourages collaboration* between developers, QA and non-technical or business participants in a software project.
- Helps your tests tell a story of the feature with an emphasis on UX

#### Benefits of BDD ✅ 
- *Makes tests human readable,* meaning they are clearer and easier to understand, which makes them easier to maintain and fix any potential bugs. 📖 
- Test cases can become documentation of the feature, in a well written test you don't have to read the code to understand what it does
- Forcing more collaboration between technical and non technical team members means a shared understanding of the requirements and overall a better piece of work
- You can catch unhandled use cases or possible feature enhancements early, saving precious time.

### Homework
Check out this awesome exercises from thoughtbot to help you level up
https://thoughtbot.com/upcase/fundamentals-of-tdd

### Resources
https://jardo.dev/tdd-on-the-shoulders-of-giants
https://jardo.dev/dont-assert-return-types-tdd
https://www.linkedin.com/pulse/introduction-test-driven-development-tdd-ruby-rails-amit-patel
https://www.andolasoft.com/blog/rails-things-you-must-know-about-tdd-and-bdd.html