Token导航 LogoToken导航TokenDH.com
开发需要联网github未标认证来源可访问许可证需确认审计通过

elixir-tddElixir TDD 命令行

Agent Skill

elixir-tdd 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

419

周安装

18

GitHub Stars

公开资料未说明

下载量

147
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:elixir-tdd(Elixir TDD 命令行)
来源仓库:https://github.com/hwatkins/my-skills
仓库路径:skills/elixir-tdd
安装命令:
npx skills add https://github.com/hwatkins/my-skills --skill elixir-tdd
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/hwatkins/my-skills --skill elixir-tdd

简介

elixir-tdd 强制执行测试驱动开发流程,要求每项改动均以失败测试开始。

  • 适用于在 Codex、Claude、Cursor、Gemini CLI 中确保 Elixir 与 Phoenix 项目的测试覆盖率。
  • 遵循红-绿-重构循环,使用 ExUnit 编写行为描述清晰的测试用例。
  • 安装前请确认项目已配置 test 脚本,并准备好夹具工厂与外部依赖 mocking 方案。
  • elixir-tdd 属于开发类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

Elixir TDD Enforcement

Strict test-driven development practices for Elixir and Phoenix projects.

The Golden Rule

No Code Without a Failing Test First

This is not optional. This is not negotiable. Every feature, every bug fix, every change starts with a test.

The TDD Cycle

  1. Red: Write a test that describes the behavior you want. Run it. It must fail.
  2. Green: Write the minimum code to make the test pass. Nothing more.
  3. Refactor: Clean up while keeping tests green.
  4. Repeat
# Step 1: Write the failing test
test "create_user/1 with valid attrs creates a user" do
  attrs = %{email: "test@example.com", name: "Test User"}
  assert {:ok, %User{} = user} = Accounts.create_user(attrs)
  assert user.email == "test@example.com"
end

# Step 2: Run it - it MUST fail
# $ mix test test/my_app/accounts_test.exs:10
# ** (UndefinedFunctionError) function Accounts.create_user/1 is undefined

# Step 3: Write minimum code to pass
def create_user(attrs) do
  %User{}
  |> User.changeset(attrs)
  |> Repo.insert()
end

# Step 4: Run test again - it passes
# Step 5: Refactor if needed, keeping tests green

Test File Structure

Match your source structure:

lib/my_app/accounts/user.ex      → test/my_app/accounts/user_test.exs
lib/my_app/accounts.ex           → test/my_app/accounts_test.exs
lib/my_app_web/live/task_live.ex → test/my_app_web/live/task_live_test.exs
lib/my_app_web/controllers/      → test/my_app_web/controllers/

Mandatory Test Cases

For every function, test:

1. Happy Path

Valid input produces expected output.

test "create_task/1 with valid attrs creates task" do
  attrs = %{title: "Test Task", status: :todo}
  assert {:ok, %Task{} = task} = Tasks.create_task(attrs)
  assert task.title == "Test Task"
  assert task.status == :todo
end

2. Validation Failures

Invalid input returns error changeset.

test "create_task/1 with missing title returns error" do
  assert {:error, %Ecto.Changeset{} = changeset} = Tasks.create_task(%{})
  assert %{title: ["can't be blank"]} = errors_on(changeset)
end

test "create_task/1 with invalid status returns error" do
  attrs = %{title: "Test", status: :invalid_status}
  assert {:error, changeset} = Tasks.create_task(attrs)
  assert %{status: ["is invalid"]} = errors_on(changeset)
end

3. Edge Cases

Boundary conditions and unusual inputs.

test "create_task/1 with empty string title returns error" do
  assert {:error, changeset} = Tasks.create_task(%{title: ""})
  assert %{title: ["can't be blank"]} = errors_on(changeset)
end

test "create_task/1 with whitespace-only title returns error" do
  assert {:error, changeset} = Tasks.create_task(%{title: "   "})
  assert %{title: ["can't be blank"]} = errors_on(changeset)
end

test "list_tasks/0 returns empty list when no tasks exist" do
  assert [] = Tasks.list_tasks()
end

4. Authorization

Users can only access their own resources.

test "get_task/2 returns error when task belongs to another user" do
  other_user = user_fixture()
  task = task_fixture(user_id: other_user.id)
  user = user_fixture()

  assert {:error, :not_found} = Tasks.get_task(user, task.id)
end

test "update_task/3 returns error when user doesn't own task" do
  owner = user_fixture()
  other_user = user_fixture()
  task = task_fixture(user_id: owner.id)

  assert {:error, :unauthorized} = Tasks.update_task(other_user, task, %{title: "Hacked"})
end

5. State Transitions (if applicable)

Valid and invalid state changes.

describe "transition_task/2" do
  test "allows todo -> in_progress" do
    task = task_fixture(status: :todo)
    assert {:ok, task} = Tasks.transition_task(task, :in_progress)
    assert task.status == :in_progress
  end

  test "allows in_progress -> done" do
    task = task_fixture(status: :in_progress)
    assert {:ok, task} = Tasks.transition_task(task, :done)
    assert task.status == :done
  end

  test "rejects todo -> done (must go through in_progress)" do
    task = task_fixture(status: :todo)
    assert {:error, :invalid_transition} = Tasks.transition_task(task, :done)
  end

  test "rejects done -> todo" do
    task = task_fixture(status: :done)
    assert {:error, :invalid_transition} = Tasks.transition_task(task, :todo)
  end
end

Testing LiveView

Use Phoenix.LiveViewTest for integration tests.

import Phoenix.LiveViewTest

describe "TaskLive.Index" do
  test "renders task list", %{conn: conn} do
    task = task_fixture(title: "My Task")
    {:ok, view, html} = live(conn, ~p"/tasks")

    assert html =~ "My Task"
    assert has_element?(view, "#task-#{task.id}")
  end

  test "creates new task", %{conn: conn} do
    {:ok, view, _html} = live(conn, ~p"/tasks")

    view
    |> form("#task-form", task: %{title: "New Task"})
    |> render_submit()

    assert has_element?(view, "#tasks", "New Task")
  end

  test "validates task on change", %{conn: conn} do
    {:ok, view, _html} = live(conn, ~p"/tasks/new")

    html =
      view
      |> form("#task-form", task: %{title: ""})
      |> render_change()

    assert html =~ "can't be blank"
  end

  test "deletes task", %{conn: conn} do
    task = task_fixture(title: "Delete Me")
    {:ok, view, _html} = live(conn, ~p"/tasks")

    view
    |> element("#task-#{task.id} button", "Delete")
    |> render_click()

    refute has_element?(view, "#task-#{task.id}")
  end
end

Test Data

Use factories or fixture functions. Keep them minimal.

# test/support/fixtures/accounts_fixtures.ex
defmodule MyApp.AccountsFixtures do
  def user_fixture(attrs \\ %{}) do
    {:ok, user} =
      attrs
      |> Enum.into(%{
        email: "user#{System.unique_integer()}@example.com",
        name: "Test User"
      })
      |> MyApp.Accounts.create_user()

    user
  end
end

# Or with ExMachina
defmodule MyApp.Factory do
  use ExMachina.Ecto, repo: MyApp.Repo

  def user_factory do
    %MyApp.Accounts.User{
      email: sequence(:email, &"user#{&1}@example.com"),
      name: "Test User"
    }
  end
end

What NOT to Do

❌ Don't write tests after the code

# WRONG: Code first, then tests
def create_task(attrs), do: ...  # Written first
test "create_task works" do ...  # Added later to "cover" it

❌ Don't skip tests for "simple" functions

# WRONG: "It's too simple to test"
def full_name(user), do: "#{user.first_name} #{user.last_name}"
# Still needs tests! What if first_name is nil?

❌ Don't test private functions directly

# WRONG: Testing private implementation
test "parse_date/1 parses ISO format" do
  assert MyModule.parse_date("2024-01-01") == ~D[2024-01-01]
end

# RIGHT: Test through the public API
test "create_event/1 accepts ISO date strings" do
  assert {:ok, event} = Events.create_event(%{date: "2024-01-01"})
  assert event.date == ~D[2024-01-01]
end

❌ Don't mock Ecto or database in context tests

# WRONG: Mocking the repo
expect(Repo, :insert, fn _ -> {:ok, %User{}} end)

# RIGHT: Use the sandbox, test real behavior
assert {:ok, %User{}} = Accounts.create_user(valid_attrs)
assert Repo.get(User, user.id)  # Actually in database

❌ Don't write tests that pass regardless of implementation

# WRONG: Test always passes
test "does something" do
  result = MyModule.do_thing()
  assert result  # What if result is {:error, ...}? Still truthy!
end

# RIGHT: Assert specific expectations
test "returns ok tuple with user" do
  assert {:ok, %User{email: "test@example.com"}} = MyModule.do_thing()
end

Pre-Implementation Checklist

Before writing ANY code, ask yourself:

  1. ☐ Have I written a failing test?
  2. ☐ Does the test describe the behavior I want?
  3. ☐ Have I run the test and confirmed it fails?
  4. ☐ Does it fail for the RIGHT reason?

Only after checking all boxes: write the implementation.

Test Organization

defmodule MyApp.TasksTest do
  use MyApp.DataCase

  alias MyApp.Tasks
  alias MyApp.Tasks.Task

  import MyApp.AccountsFixtures
  import MyApp.TasksFixtures

  describe "create_task/1" do
    test "with valid attrs creates task" do
      # ...
    end

    test "with invalid attrs returns error changeset" do
      # ...
    end

    test "with empty title returns error" do
      # ...
    end
  end

  describe "update_task/2" do
    setup do
      task = task_fixture()
      %{task: task}
    end

    test "with valid attrs updates the task", %{task: task} do
      # ...
    end
  end

  describe "delete_task/1" do
    # ...
  end
end

Mocking External Dependencies with Mox

Use behaviours + Mox for external services. Never mock Ecto or internal modules.

# 1. Define a behaviour
defmodule MyApp.WeatherAPI do
  @callback get_forecast(String.t()) :: {:ok, map()} | {:error, term()}
end

# 2. Create the real implementation
defmodule MyApp.WeatherAPI.Client do
  @behaviour MyApp.WeatherAPI

  @impl true
  def get_forecast(city) do
    Req.get("https://api.weather.com/forecast", params: [city: city])
  end
end

# 3. Configure the mock in test_helper.exs
Mox.defmock(MyApp.MockWeatherAPI, for: MyApp.WeatherAPI)

# 4. Configure in config/test.exs
# config :my_app, weather_api: MyApp.MockWeatherAPI

# 5. Use in your context (reads from config)
defmodule MyApp.Weather do
  def weather_api, do: Application.get_env(:my_app, :weather_api, MyApp.WeatherAPI.Client)

  def get_forecast(city) do
    weather_api().get_forecast(city)
  end
end

# 6. Test with mock
import Mox

test "get_forecast returns weather data" do
  expect(MyApp.MockWeatherAPI, :get_forecast, fn "London" ->
    {:ok, %{temp: 15, condition: "cloudy"}}
  end)

  assert {:ok, %{temp: 15}} = Weather.get_forecast("London")
end

Property-Based Testing with StreamData

Test properties that hold for all inputs, not just specific examples:

use ExUnitProperties

# ✅ Good: Test a property that always holds
property "User.full_name/1 always returns a string" do
  check all first <- string(:alphanumeric, min_length: 1),
            last <- string(:alphanumeric, min_length: 1) do
    user = %User{first_name: first, last_name: last}
    result = User.full_name(user)
    assert is_binary(result)
    assert String.contains?(result, first)
    assert String.contains?(result, last)
  end
end

# ✅ Good: Roundtrip property
property "encoding then decoding returns the original" do
  check all data <- map_of(string(:alphanumeric), integer()) do
    assert data == data |> MyApp.Encoder.encode() |> MyApp.Encoder.decode()
  end
end

# ✅ Good: Invariant property
property "sorting is idempotent" do
  check all list <- list_of(integer()) do
    sorted = Enum.sort(list)
    assert sorted == Enum.sort(sorted)
  end
end

# ✅ Good: Custom generators for domain types
property "valid emails are accepted, invalid rejected" do
  valid_email = gen all name <- string(:alphanumeric, min_length: 1),
                        domain <- string(:alphanumeric, min_length: 1) do
    "#{name}@#{domain}.com"
  end

  check all email <- valid_email do
    assert {:ok, _} = Accounts.validate_email(email)
  end
end

Running Tests

# Run all tests
mix test

# Run specific file
mix test test/my_app/tasks_test.exs

# Run specific test by line number
mix test test/my_app/tasks_test.exs:42

# Run with coverage
mix test --cover

# Run failed tests only
mix test --failed

# Run tests matching a pattern
mix test --only integration

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

37.33%
按下载量换算55

Claude

31.18%
按下载量换算46

Cursor

17.71%
按下载量换算26

Gemini CLI

9.64%
按下载量换算14

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills