Facebook

Trest Driven AI Coding

The Lexicon: Understanding the Lingo

Before diving deep, let’s make sure we’re speaking the same language. These are common terms you’ll encounter:

  • Test-Driven Development (TDD): A software development process where you write tests before you write the actual code. It follows a short, repetitive cycle.
  • Red-Green-Refactor: The core cycle of TDD:
    • RED: Write a small test that defines a tiny piece of desired functionality. It will fail because the code doesn’t exist yet.
    • GREEN: Write the simplest possible code to make that one test pass.
    • REFACTOR: Clean up the code you just wrote (and any related code) without changing its behavior, making it more readable, efficient, or better structured. Your tests ensure you don’t break anything.
  • Unit Test: A test for a very small, isolated piece of code (a “unit”), like a single function or method. It checks if that specific unit works correctly on its own.
  • Integration Test: A test that checks if different parts (units or modules) of your application work together correctly. For example, does your ContactService correctly interact with a (mocked or real) Storage module?
  • End-to-End (E2E) Test: A test that validates the entire application flow from the user’s perspective, like simulating a user clicking through the UI or running a CLI command and checking the final output.
  • Mocking: Creating “fake” versions of dependent components (like a database or an external service) for your unit tests. This allows you to test a unit in isolation, making tests faster and more reliable. (e.g., unittest.mock.MagicMock in Python).
  • User Story: A short, simple description of a feature told from the perspective of the person who desires the new capability, usually a user or customer. Typically follows the format: “As a [type of user], I want [an action] so that [a benefit/value].”
  • Acceptance Criteria (for User Stories): Specific, testable conditions that must be met for a user story to be considered “done.” Often written in a “Given-When-Then” format (like Gherkin). These directly inform your tests.
  • Gherkin: A simple, human-readable language used to describe software behavior without detailing how that behavior is implemented. Often used for writing acceptance criteria and E2E test scenarios (e.g., Scenario: Show contact details by ID).
  • Lean (in Software): A philosophy focused on maximizing customer value while minimizing waste (e.g., unused features, gold-plating, delays). It emphasizes learning and adapting quickly.
  • Build-Measure-Learn: The core feedback loop of Lean. Build a small version (or experiment), measure how users react/how it performs, learn from that data, and then decide what to build or change next.
  • Minimum Viable Product (MVP): The version of a new product that allows a team to collect the maximum amount of validated learning about customers with the least effort. It’s not a crappy version of the final product; it’s the smallest thing you can build to start learning.

A Brief History of Standards We Use (and Why)

Many modern software development practices didn’t appear out of thin air. They evolved to solve real problems.

  • Agile Manifesto (2001):
    • What/Where: A group of 17 software developers met in Snowbird, Utah, frustrated with heavyweight, documentation-driven development processes that were slow and often resulted in software that didn’t meet user needs. They created the “Manifesto for Agile Software Development.”
    • Why: To promote better ways of developing software by valuing:
      • Individuals and interactions over processes and tools
      • Working software over comprehensive documentation
      • Customer collaboration over contract negotiation
      • Responding to change over following a plan
    • This mindset underpins many practices, including TDD, Lean, and Scrum.
  • Extreme Programming (XP) (Late 1990s):
    • What/Where: Pioneered by Kent Beck (and others like Ward Cunningham and Ron Jeffries). TDD is a core practice of XP.
    • Why: XP aimed to improve software quality and responsiveness to changing customer requirements. Practices like TDD, pair programming, continuous integration, and small releases were introduced to provide rapid feedback, build quality in, and foster team collaboration. TDD specifically helps ensure code is testable, provides a safety net for changes, and can drive better design.
  • Lean (applied to Software, early 2000s):
    • What/Where: Mary and Tom Poppendieck adapted Lean manufacturing principles (from Toyota Production System) to software development in their book “Lean Software Development: An Agile Toolkit” (2003).
    • Why: To reduce “waste” in software development. Waste includes building the wrong thing, building too much, delays, and defects. The “Build-Measure-Learn” loop, MVPs, and focusing on delivering customer value quickly are key Lean concepts.
  • Behavior-Driven Development (BDD) (Mid-2000s):
    • What/Where: An evolution of TDD, promoted by Dan North. Gherkin is a common language used in BDD.
    • Why: BDD aims to improve communication between technical and non-technical stakeholders by focusing on the behavior of the software from the user’s perspective. It encourages writing tests (or specifications) in a natural, understandable language (like Gherkin) before writing code, ensuring everyone has a shared understanding of what needs to be built.

These practices emerged because older methods were often slow, inflexible, and didn’t always deliver what users needed. The common thread is getting faster feedback, improving quality, collaborating effectively, and delivering value to the user.


The Simplest Path to Getting Test-Driven Development Going

TDD can feel a bit backward at first. Here’s the simplest way to start:

  1. Pick ONE Tiny Thing: Don’t try to test your whole application at once. Choose the smallest, simplest piece of functionality you want to add or change.
    • Example: You want to write a function that takes two numbers and returns their sum.
  2. Think: “How Would I Know It Works?” (Write the RED Test):
    • Before writing any code for the function itself, open your test file.
    • Write a test that calls your (not-yet-existing) function with specific inputs and asserts (checks) that it produces the expected output.
      # In test_my_math.py
      import unittest
      # from my_math import add # This will initially fail, which is good!
      
      class TestMyMath(unittest.TestCase):
          def test_add_two_positive_numbers(self):
              # from my_math import add # Or import at top if my_math.py exists
              # For the very first run, you might not even have my_math.py or add()
              # self.assertEqual(add(2, 3), 5) # This is your goal
      
              # To make it truly RED initially if `add` doesn't exist:
              import my_math # Assume my_math.py is empty for now
              self.assertEqual(my_math.add(2, 3), 5)
      
    • Run your tests. It MUST FAIL (Red). If it doesn’t fail, your test isn’t testing what you think, or the functionality somehow already exists. The failure tells you, “Okay, the system is ready for me to add this specific behavior.”
  3. Write the Simplest Code to Make It Pass (Go GREEN):
    • Now, go to your actual code file (e.g., my_math.py).
    • Write the absolute minimum amount of code to make that one failing test pass.
      # In my_math.py
      def add(a, b):
          return a + b
      
    • Run your tests again. It should now PASS (Green). Congratulations!
  4. Clean It Up (REFACTOR):
    • Look at the code you just wrote (and any related code).
    • Is it clear? Is there any duplication? Could it be named better?
    • Make small improvements without changing what it does.
    • Run your tests again after each small refactoring to ensure they still pass. This is your safety net.
    • For our simple add function, there might not be much to refactor initially.
  5. Repeat for the Next Tiny Thing:
    • What’s another behavior for add? Adding a negative number?
      • RED: Write a new test: self.assertEqual(my_math.add(5, -2), 3). Run tests (new one fails, old one passes).
      • GREEN: Your current add function should already make this pass. Run tests (all pass).
      • REFACTOR: Still simple, likely no refactoring needed.
    • What about adding zero?
      • RED: Write self.assertEqual(my_math.add(7, 0), 7). Run tests.
      • GREEN: Current code makes it pass. Run tests.
      • REFACTOR: No change.

Key Mindset for Starting:

  • Baby Steps: TDD is about very small, incremental steps.
  • Test First, Always: Resist the urge to write the application code before you have a failing test that demands it.
  • One Thing at a Time: Each test should generally focus on one specific aspect or behavior.
  • Don’t Aim for Perfection Immediately: The “Green” step is just about passing the test. Perfection and elegance come during “Refactor.”
  • Tools are Secondary: You can do TDD with any language and a basic testing framework. The process and mindset are what matter.
Scroll to Top