Skip to content

Day 046 — Coverage & testify

Month 2 · Week 3 · ⬅ Day 045 · Day 047 ➡ · Journal index

🎯 Learning Objective

Measure test coverage with the go toolchain and visualize gaps, then understand what the popular third-party testify library adds (and why the stdlib often suffices).

📚 Topics

  • go test -cover, -coverprofile, go tool cover -html/-func
  • -covermode (set/count/atomic) · cross-package coverage
  • testify (assert, require, suite, mock) — third-party

📖 Reading / Sources

📝 Notes

  • go test -cover ./... prints a per-package coverage percentage. It measures statement coverage, not branch or path coverage — 100% statements can still miss logic. → [[coverage-is-not-correctness]]
  • Save a profile and inspect it:
  • go test -coverprofile=cover.out ./...
  • go tool cover -func=cover.out → per-function percentages + total.
  • go tool cover -html=cover.out → green/red source view in the browser.
  • -covermode: set (did it run? default), count (how many times), atomic (count, race-safe — required with -race).
  • By default a package's tests only cover that package. Use -coverpkg=./... to credit coverage of helpers exercised across package boundaries.
  • Chase meaningful coverage: error branches, edge cases, the case you'd forget. Don't write tests just to color a line green.
  • testify is the most common third-party assertion lib. assert.Equal(t, want, got) logs and continues (like t.Error); require.Equal stops the test (like t.Fatal). suite gives setup/teardown structs; mock builds fakes. It's a dependency — the stdlib if got != want { t.Errorf(...) } is zero-dep and idiomatic.
  • For value comparison without testify, the stdlib offers reflect.DeepEqual; in newer code google/go-cmp's cmp.Diff (third-party) gives readable diffs.

💻 Code Examples

testify is third-party, so it lives here as a snippet, not a runnable repo example:

import (
    "testing"

    "github.com/stretchr/testify/assert"  // go get github.com/stretchr/testify
    "github.com/stretchr/testify/require"
)

func TestWithTestify(t *testing.T) {
    got, err := Parse("42")
    require.NoError(t, err)      // require: stop here if it errored (like t.Fatal)
    assert.Equal(t, 42, got)     // assert: log + continue (like t.Error)
    assert.Len(t, []int{1, 2}, 2)
}

The stdlib-only equivalent (no dependency), which I prefer for this repo:

func TestWithStdlib(t *testing.T) {
    got, err := Parse("42")
    if err != nil {
        t.Fatalf("Parse: unexpected error: %v", err)
    }
    if got != 42 {
        t.Errorf("Parse = %d; want 42", got)
    }
}

Run coverage over this week's exercises: go test -coverprofile=cover.out ./exercises/month-02/week-3/... && go tool cover -func=cover.out

🏋️ Exercises / Practice

Exercise Status Link
Get reverse/greeter/wordfreq to high meaningful coverage exercises/month-02/week-3
Inspect go tool cover -html for the 405 branch exercises/month-02/week-3/greeter

🐛 Mistakes Made

  • Treated "100% coverage" as "correct" — coverage is statement-level; a wrong branch with a passing assertion still hits 100%.
  • Ran -race with the default covermode and got a warning; -covermode=atomic is required with -race.

❓ Open Questions

  • Is go-cmp's cmp.Diff worth a dependency over reflect.DeepEqual for big structs? (Likely yes for readable diffs in CI.)

🧠 Active Recall (answer without looking)

  1. Q: What does go test -cover actually measure?
    A

Statement coverage — which statements executed. Not branch/path coverage, so 100% does not prove correctness. 2. Q: assert vs require in testify?

A

assert.* logs the failure and continues (like t.Error); require.* fails and stops the test immediately (like t.Fatal).

🪶 Feynman Reflection

Coverage is a heat map of which lines your tests touched, not a proof they're right. go tool cover -html paints untouched lines red so you can aim new cases at real gaps. testify is sugar over t.Error/t.Fatal; nice ergonomics, but a dependency the stdlib doesn't need.

🕳️ Knowledge Gaps

  • Branch/mutation testing tools (e.g. go-mutesting) — beyond statement coverage.

✅ Summary

I can produce and read coverage profiles, target real gaps, and explain what testify adds versus the zero-dependency stdlib assertion style.

⏭️ Next Steps / Prep for Tomorrow

  • Day 047: micro-benchmarks with testing.B, b.N, b.ReportAllocs, and benchstat.

Time spent Difficulty Confidence
90 min 🟦🟦⬜⬜⬜ 🟦🟦🟦⬜⬜

Suggested commit: test(week-3): coverage profiling and testify notes (day 046)