coredns/coredns
Testing
CoreDNS has three test surfaces: unit tests next to each plugin, integration tests under test/, and fuzz harnesses (*/fuzz.go). All of them run in CI.
Quick reference
go test ./... # everything
go test -race ./plugin/cache/... # one plugin with race detector
go test -run TestServeDNS ./test # one integration test
go test -count=1 ./... # disable test result cache
go test -tags=fuzz ./plugin/forward # run fuzz-only test filesgo test -race is what CI uses. Race-test failures often surface in cache, forward, and kubernetes because of their goroutine-heavy designs.
Unit tests
Live next to the code they cover:
plugin/cache/
├── cache.go
├── cache_test.go
├── handler.go
├── handler_test.go (none — covered by cache_test.go)
├── prefetch_test.go
├── setup_test.go
├── ...Plugins typically expose a parse (or similarly-named) helper that takes a Caddyfile string and returns the configured plugin instance. Tests build a caddy.Controller from a string and assert on the resulting object:
c := caddy.NewTestController("dns", `cache 30 { prefetch 5 }`)
ca, err := cacheParse(c)Other patterns:
dnstest.NewRecorder(w)fromplugin/pkg/dnstestcaptures the response a plugin writes.plugin/pkg/nonwriteris a buffering writer for tests that just want to inspect the produced message.- Most plugins ship a tiny
log_test.gothat initialises the plugin's logger so*_test.gofiles can calllog.D.Set()without duplication.
Integration tests
test/ contains 51 *_test.go files that spin up a real CoreDNS in-process via test.Server (test/server.go). They cover Corefile parsing edge cases and end-to-end behaviours across plugin combinations.
Examples:
| File | What it tests |
|---|---|
test/auto_test.go |
auto plugin watching a temp directory |
test/etcd_test.go |
etcd backend (requires a local etcd) |
test/forward_test.go, test/proxy_test.go, test/proxy_health_test.go |
Forwarder behaviours |
test/grpc_test.go, test/quic_test.go, test/https_test.go, test/https3_test.go, test/tls_test.go |
Encrypted transports |
test/multisocket_test.go |
SO_REUSEPORT listener parallelism |
test/reload_test.go |
Caddy graceful reload |
test/view_test.go |
View filtering |
test/wildcard_test.go, test/server_reverse_test.go |
Zone match edge cases |
test/presubmit_test.go |
Repository-wide checks (no proxy references, every plugin has a README, etc.) |
The presubmit_test.go file is interesting: it asserts on the shape of the repo, not on runtime behaviour. It will fail if you forget to add a README.md for a new plugin, or if you accidentally introduce the deprecated proxy directive in tests.
test/server.go provides test.Server (a wrapper around caddy.Start) and test.TempFile (creates a temp file with given contents). Pretty much every integration test starts with one of these.
Fuzz tests
Five plugins ship fuzz harnesses:
| File | Targets |
|---|---|
plugin/forward/fuzz.go |
Forward parser |
plugin/cache/fuzz.go |
Cache key/insert |
plugin/file/fuzz.go |
Master-file parser |
plugin/kubernetes/object/... (where applicable) |
object parsing |
test/fuzz_corefile.go |
Corefile parser |
Fuzz harnesses are wired into OSS-Fuzz via .github/workflows/cifuzz.yml. The cifuzz workflow runs each harness for a short time on every PR.
Mocking and helpers
| Package | What it provides |
|---|---|
plugin/pkg/dnstest |
NewRecorder, NewServer, Multiplexer |
plugin/pkg/nonwriter |
A dns.ResponseWriter that just stores |
plugin/test |
Case struct for table-driven DNS exchange tests, helpers for dns.RR slices |
plugin/file/example_org.go |
A pre-built example.org zone for backend tests |
A typical table-driven test:
cases := []test.Case{
{Qname: "a.example.org.", Qtype: dns.TypeA,
Answer: []dns.RR{test.A("a.example.org. IN A 127.0.0.1")}},
}
for _, tc := range cases {
m := tc.Msg()
rec := dnstest.NewRecorder(&test.ResponseWriter{})
h.ServeDNS(ctx, rec, m)
test.SortAndCheck(t, rec.Msg, tc)
}What CI runs
The Go test workflow (.github/workflows/go.test.yml) does, in order:
- Set up Go (matrix on a few minor versions).
make genandgit diff --exit-codeto ensure committed generated files are current.go test -race ./....- Optionally upload coverage.
Failures usually fall into two buckets:
- Generated file drift — solved by
make genand committing the result. - Race conditions — usually surface in
forwardorcache. The race detector is unforgiving.
The cifuzz.yml workflow runs fuzz harnesses against the diff. The make.doc.yml workflow runs make -f Makefile.doc and asserts no diff. The golangci-lint.yml workflow runs the configured linters.
Local performance loop
CoreDNS doesn't ship a benchmark suite per se, but most plugins have Benchmark* tests:
go test -bench=. -benchmem -run='^$' ./plugin/cache/...
go test -bench=. -benchmem -run='^$' ./plugin/forward/...benchstat from golang.org/x/perf/cmd/benchstat is used to compare before/after runs. Results are usually reported in PR descriptions when claiming a performance improvement.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.