Skip to content
elixir-tdd-enforcement logo

Elixir TDD Enforcement: The Iron Law

elixir-tdd-enforcement

MANDATORY for ANY feature or bugfix - write ExUnit test FIRST, watch it FAIL, then implement. NO exceptions. Use before writing any Elixir production code.

SKILL.md

Full skill instructions

Elixir TDD Enforcement: The Iron Law

THE IRON LAW

NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST

Not sometimes. Not usually. ALWAYS.

If you write production code before a failing test, DELETE IT and start over.

WHEN THIS SKILL APPLIES

  • Implementing ANY new function
  • Fixing ANY bug
  • Adding ANY feature
  • Modifying ANY behavior
  • Refactoring ANY code

If you're changing .ex files in lib/, this skill is MANDATORY.

THE RED-GREEN-REFACTOR CYCLE

Phase 1: RED (Write Failing Test)

  1. Write ONE minimal ExUnit test

    test "creates user with valid attrs" do
      attrs = %{name: "Alice", email: "[email protected]"}
      assert {:ok, %User{} = user} = Accounts.create_user(attrs)
      assert user.name == "Alice"
      assert user.email == "[email protected]"
    end
    
  2. Run the test

    mix test test/​my_app/​accounts_test.exs:42
    
  3. VERIFY IT FAILS FOR THE RIGHT REASON

    • Read the error message
    • Confirm it's failing because functionality doesn't exist
    • NOT because of syntax errors or wrong test setup

CHECKPOINT: If test doesn't fail, delete it and write a different test.

Phase 2: GREEN (Minimal Implementation)

  1. Write SIMPLEST code to pass the test

    def create_user(attrs) do
      %User{}
      |> User.changeset(attrs)
      |> Repo.insert()
    end
    
  2. Run the test again

    mix test test/​my_app/​accounts_test.exs:42
    
  3. VERIFY IT PASSES

    • Read the actual output
    • See the green dot or "1 test, 0 failures"
    • NOT just assume it works

CHECKPOINT: If test doesn't pass, fix implementation (not test).

Phase 3: REFACTOR (Improve While Green)

  1. Improve code quality

    • Extract functions
    • Improve names
    • Add pattern matching
  2. Run tests after EACH change

    mix test
    
  3. Stay GREEN

    • If tests fail during refactor, undo
    • Only refactor when all tests pass

CHECKPOINT: Tests must stay green throughout refactoring.

VERIFICATION CHECKLIST

Before claiming you're done, verify:

  • I wrote the test BEFORE any implementation code
  • I watched the test FAIL for the right reason
  • I read the actual failure message
  • I implemented only enough code to pass the test
  • I ran the test again and saw it PASS
  • I read the actual success message
  • All other tests still pass
  • I refactored only while tests were green

If you can't check ALL boxes, you didn't follow TDD.

COMMON VIOLATIONS AND RESPONSES

Violation: "I'll just write the code, then write the test"

Response: NO. Delete the code. Write test first.

Violation: "The function is simple, I don't need to see it fail"

Response: WRONG. Even simple code needs failing tests. Write test, watch fail.

Violation: "I already know what the test will look like"

Response: Irrelevant. Write it first anyway.

Violation: "I wrote the test and implementation together"

Response: Delete both. Write test, watch fail, then implement.

Violation: "The test passed on first run"

Response: RED FLAG. Test might not be testing anything. Review test.

Violation: "I'm just refactoring, I don't need new tests"

Response: Correct - but ALL existing tests must stay GREEN.

ELIXIR-SPECIFIC TEST PATTERNS

Testing Context Functions

# RED: Write test first
test "list_users/​0 returns all users" do
  user1 = fixture(:user)
  user2 = fixture(:user)
  users = Accounts.list_users()
  assert length(users) == 2
  assert user1 in users
  assert user2 in users
end

# Run test → watch it fail (function doesn't exist)

# GREEN: Implement
def list_users do
  Repo.all(User)
end

# Run test → watch it pass

Testing Changesets

# RED: Write test for validation
test "changeset with invalid email" do
  changeset = User.changeset(%User{}, %{email: "invalid"})
  refute changeset.valid?
  assert %{email: ["invalid format"]} = errors_on(changeset)
end

# Run test → watch it fail

# GREEN: Add validation
def changeset(user, attrs) do
  user
  |> cast(attrs, [:email])
  |> validate_format(:email, ~r/​@/)
end

Testing Phoenix Controllers

# RED: Write test
test "GET /​users returns 200", %{conn: conn} do
  conn = get(conn, ~p"/​users")
  assert html_response(conn, 200)
end

# Run test → watch it fail (route doesn't exist)

# GREEN: Add route and controller action

DIALYZER ERRORS: SPECIAL CASE

If Dialyzer reports an error:

  1. Write a test that exercises the problematic code
  2. Make sure test passes (proving code works)
  3. Add @spec to guide Dialyzer
  4. Run mix dialyzer to verify

NEVER:

  • Add to dialyzer.ignore
  • Modify dialyzer PLT to suppress
  • Comment out the code

The test proves it works. The spec helps Dialyzer understand.

CREDO WARNINGS: SPECIAL CASE

If Credo reports a warning:

  1. Understand WHY it's warning
  2. Fix the actual issue (complexity, style, etc.)
  3. Run mix credo to verify

NEVER:

  • Add to .credo.exs disabled list
  • Use inline # credo:disable-for-this-file
  • Ignore the warning

Credo is helping you write better code. Listen to it.

THE DISCIPLINE

TDD feels slow at first. That's because you're used to:

  • Writing code fast (then debugging for hours)
  • Skipping tests (then breaking things in production)
  • Guessing if it works (then finding out it doesn't)

TDD is actually faster because:

  • Tests catch bugs immediately
  • You know exactly what to implement
  • Refactoring is safe
  • Code works the first time

ENFORCEMENT

Before writing ANY Elixir production code, ask:

  1. "Have I written a failing test for this?"
  2. "Have I actually RUN the test and seen it fail?"
  3. "Do I know WHY it's failing?"

If any answer is NO → write the test first.

REMEMBER

"Tests that pass on the first run might not be testing anything."

"Code without a failing test first is guess-driven development."

"TDD is slow. Debugging untested code is slower."

THE RULE

RED → GREEN → REFACTOR

Not GREEN → RED → "oops"

Not WRITE → PRAY → DEBUG

RED → GREEN → REFACTOR

Every. Single. Time.

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View