Skip to content
Back to skills

Elixir Tdd Enforcement

ASecurity

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

  • 666 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added February 8, 2026
developmentbashtestingdebuggingrefactoring

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill elixir-tdd-enforcement --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Elixir Tdd Enforcement?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Elixir Tdd Enforcement
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-elixir-tdd-enforcement/badge)](https://www.skillsdirectory.com/skills/majiayu000-elixir-tdd-enforcement)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: elixir-tdd-enforcement
description: MANDATORY for ANY feature or bugfix - write ExUnit test FIRST, watch it FAIL, then implement. NO exceptions. Use before writing any Elixir production code.
---

# 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**
   ```elixir
   test "creates user with valid attrs" do
     attrs = %{name: "Alice", email: "alice@example.com"}
     assert {:ok, %User{} = user} = Accounts.create_user(attrs)
     assert user.name == "Alice"
     assert user.email == "alice@example.com"
   end
   ```

2. **Run the test**
   ```bash
   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**
   ```elixir
   def create_user(attrs) do
     %User{}
     |> User.changeset(attrs)
     |> Repo.insert()
   end
   ```

2. **Run the test again**
   ```bash
   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**
   ```bash
   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
```elixir
# 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
```elixir
# 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
```elixir
# 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.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.