Chisle
The coding-agent plugin that bills 52% of a bare model across 20 live tasks. Terse persona + tool-output compressor + context diet.
翻訳待ち
A simple and extensible behavioural testing library for Go. You can use api test to simplify REST API, HTTP handler and e2e tests.
翻訳待ち
開発ツールGo · MakefileGitHub @steinfletcher自分でインストール所有者未確認
Paste into Claude or ChatGPT for installation instructions suited to your environment.
Have you tried it? One tap is enough. No sign-in needed.
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗A simple and extensible behavioural testing library for Go HTTP services. Tests are written the way a client uses the API: build a request, send it to the handler, and assert on the response. External HTTP calls can be mocked, and every test can render a sequence diagram of what happened.
In behavioural tests the internal structure of the app is not known by the tests. Data is input to the system and the outputs are expected to meet certain conditions.
http.Handler, or against a running servertesting, Ginkgo and custom assertion librariesThe API is stable. The library is maintained and issues are addressed; feature requests are considered.
Join the conversation at #apitest on https://gophers.slack.com.
Logo by @egonelbre
This README covers the whole library. The same material with more narrative is at https://apitest.dev, and the API reference is on pkg.go.dev.
This repository includes an Agent Skill that teaches coding agents how to write apitest tests, including the mistakes that are easy to make with mocks and JSONPath. In Claude Code, add the repository as a plugin marketplace and install the plugin:
claude plugin marketplace add steinfletcher/apitest
claude plugin install apitest@apitest
Inside a session, /plugin marketplace add steinfletcher/apitest followed by /plugin install apitest@apitest does the
same. To use the skill without the plugin, or with another agent that supports Agent Skills, copy
skills/apitest into the agent's skills directory, such as .claude/skills/apitest in your project.
The documentation is also available as Markdown for agents, at apitest.dev/llms-full.txt.
go get github.com/steinfletcher/apitest
func TestGetUser(t *testing.T) {
apitest.New().
Handler(handler).
Get("/user/1234").
Expect(t).
Status(http.StatusOK).
Body(`{"id": "1234", "name": "Tate"}`).
End()
}
Handler takes any http.Handler, so a http.ServeMux, a gin engine, an echo instance and so on all work. Everything
before Expect(t) describes the request; everything after it describes the expected response. End() runs the test.
When the expected body is valid JSON it is compared structurally, so key order and whitespace do not matter.
Get, Post, Put, Patch and Delete set the method and URL. Each has an f variant that formats the URL, and
Method covers anything else.
apitest.Handler(handler).Getf("/user/%s", id)
apitest.Handler(handler).Method(http.MethodOptions).URL("/user")
A ready-made *http.Request can be used instead of the builder.
req := httptest.NewRequest(http.MethodGet, "/user/1234", nil)
apitest.Handler(handler).
HttpRequest(req).
Expect(t).
Status(http.StatusOK).
End()
apitest.Handler(handler).
Get("/hello").
Header("Authorization", "Bearer token").
Headers(map[string]string{"X-Request-Id": "12345"}).
ContentType("application/json").
Expect(t).
Status(http.StatusOK).
End()
Query, QueryParams and QueryCollection can be combined. QueryCollection sets repeated parameters, so
map[string][]string{"a": {"b", "c", "d"}} is encoded as a=b&a=c&a=d. A query string written in the URL, as in
Get("/hello?page=2"), is kept, and parameters from the builder methods are appended to it.
apitest.Handler(handler).
Get("/hello").
QueryParams(map[string]string{"a": "1", "b": "2"}).
Query("c", "d").
QueryCollection(map[string][]string{"e": {"f", "g"}}).
Expect(t).
Status(http.StatusOK).
End()
apitest.Handler(handler).
Get("/hello").
Cookie("session", "12345").
Cookies(apitest.NewCookie("theme").Value("dark").Path("/")).
Expect(t).
Status(http.StatusOK).
End()
Body sets a raw body. JSON sets the body and the Content-Type header; it accepts a string, a []byte, or any
value that can be marshalled. BodyFromFile and JSONFromFile read the body from disk.
apitest.Handler(handler).
Post("/user").
JSON(map[string]any{"name": "jan", "age": 32}).
Expect(t).
Status(http.StatusCreated).
End()
GraphQL requests are built with GraphQLQuery, or GraphQLRequest when an operation name is needed.
apitest.Handler(handler).
Post("/graphql").
GraphQLQuery(`query User($id: ID!) { user(id: $id) { name } }`, map[string]any{"id": "1234"}).
Expect(t).
Status(http.StatusOK).
End()
FormData sends an application/x-www-form-urlencoded body.
apitest.Handler(handler).
Post("/hello").
FormData("a", "1").
FormData("b", "2", "3").
Expect(t).
Status(http.StatusOK).
End()
MultipartFormData and MultipartFile send multipart/form-data. The two form styles cannot be combined in one
request.
apitest.Handler(handler).
Post("/upload").
MultipartFormData("description", "holiday photos").
MultipartFile("file", "testdata/beach.jpg", "testdata/sunset.jpg").
Expect(t).
Status(http.StatusOK).
End()
Files are read from the OS by default. UseFS swaps in any fs.FS, such as an in-memory fstest.MapFS.
inMemFS := fstest.MapFS{
"audio.wav": &fstest.MapFile{Data: []byte{19, 2, 123, 12, 35, 1}},
}
apitest.Handler(handler).
UseFS(inMemFS).
Post("/upload").
MultipartFile("file", "audio.wav").
Expect(t).
Status(http.StatusOK).
End()
This is the beginning of the README. Read the rest at the original. Read the full README ↗