From gomega
Builds Go binaries, runs them as subprocesses, sends signals, and asserts on exit codes and stdout/stderr with Gomega matchers.
How this skill is triggered — by the user, by Claude, or both
Slash command
/gomega:gexecThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
`gexec` compiles Go binaries, starts them as subprocesses, sends them signals, and exposes their stdout/stderr as `gbytes.Buffer`s so you can assert on streaming output and exit codes. Import it normally (Gomega is dot-imported):
gexec compiles Go binaries, starts them as subprocesses, sends them signals, and exposes their stdout/stderr as gbytes.Buffers so you can assert on streaming output and exit codes. Import it normally (Gomega is dot-imported):
import (
. "github.com/onsi/gomega"
. "github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega/gexec"
)
Docs: https://onsi.github.io/gomega/#gexec-testing-external-processes.
gexec.Build(packagePath, ...args) runs go build and returns the path to a temp binary.
var pathToCLI string
BeforeSuite(func() {
var err error
pathToCLI, err = gexec.Build("github.com/spacely/sprockets")
Expect(err).NotTo(HaveOccurred())
})
AfterSuite(func() {
gexec.CleanupBuildArtifacts()
})
gexec.BuildWithEnvironment(packagePath, env []string, ...args) — set env vars for the build (e.g. GOOS/GOARCH for cross-compilation).gexec.BuildIn(gopath, packagePath, ...args) — build with a custom GOPATH (e.g. against vendored deps).Always defer/AfterSuite gexec.CleanupBuildArtifacts(). It deletes the temp binaries; skipping it leaks files into your temp dir across runs.
gexec.Start(cmd, outWriter, errWriter) calls cmd.Start() and returns a *gexec.Session that wraps and monitors the process, forwarding stdout/stderr to the writers you pass.
command := exec.Command(pathToCLI, "-api=127.0.0.1:8899")
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
Expect(err).NotTo(HaveOccurred())
Pass GinkgoWriter for both writers. Output is then silent on passing tests but printed on failure (and always under ginkgo -v) — invaluable for debugging.
The gexec.Exit() matcher is pollable and only works on a *Session — pair it with Eventually (→ gomega:async):
Eventually(session).Should(gexec.Exit()) // exited, any code
Eventually(session).Should(gexec.Exit(0)) // exited with code 0
session.ExitCode() returns the raw code, or -1 if the process hasn't exited yet.
session.Wait([timeout]) blocks until exit (failing if it doesn't exit within the default Eventually timeout) and returns the session for chaining:
session.Wait(5 * time.Second)
Don't Wait on the wrapped exec.Cmd yourself — gexec already calls Wait to monitor the process. session.Wait is just Eventually under the hood.
session.Kill() // SIGKILL
session.Interrupt() // SIGINT
session.Terminate() // SIGTERM
session.Signal(sig) // arbitrary os.Signal
Each returns the session, so chain with Wait: session.Terminate().Wait(). Signaling an already-exited process is a no-op.
Package-level helpers signal all sessions gexec has started, in any context — ideal for cleanup:
gexec.Kill() // SIGKILL to all
gexec.Terminate() // SIGTERM to all
gexec.Interrupt() // SIGINT to all
gexec.Signal(sig) // os.Signal to all
gexec.KillAndWait() // signal all, then wait
gexec.TerminateAndWait(2 * time.Second) // per-process timeout
AfterSuite(func() { gexec.KillAndWait() })
These are global. Calling them in an AfterEach will also signal processes started in BeforeSuite. Good practice is to ensure all processes are killed before the suite ends.
session.Out and session.Err are gbytes.Buffers connected to stdout/stderr, so use the gbytes.Say matcher for ordered streaming assertions (→ gomega:gbytes). The session itself is a BufferProvider for Out:
Eventually(session.Out).Should(gbytes.Say("hello [A-Za-z], nice to meet you"))
Eventually(session.Err).Should(gbytes.Say("oops!"))
Eventually(session).Should(gbytes.Say("hello")) // shorthand for session.Out
To grab the whole output after exit, Wait() returns the session, so .Out.Contents() gives a []byte (→ gomega:matchers):
Expect(session.Wait().Out.Contents()).To(ContainSubstring("finished successfully"))
var pathToCLI string
var _ = BeforeSuite(func() {
var err error
pathToCLI, err = gexec.Build("github.com/spacely/sprockets")
Expect(err).NotTo(HaveOccurred())
})
var _ = AfterSuite(func() {
gexec.CleanupBuildArtifacts()
})
var _ = It("greets and exits cleanly", func() {
command := exec.Command(pathToCLI, "--name=George")
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
Expect(err).NotTo(HaveOccurred())
Eventually(session).Should(gbytes.Say("hello George"))
Eventually(session).Should(gexec.Exit(0))
Expect(session.Out.Contents()).To(ContainSubstring("done"))
})
npx claudepluginhub onsi/gomega --plugin gomegaWires Ginkgo into a Go package: installs CLI, bootstraps suite_test.go, handles _test package convention, dot-imports, and *testing.T interop.
Explains the Gomega matcher/assertion library for Go: Expect/Ω notation, matchers-as-values, multi-return error idiom, sync vs async assertions, and sub-library overview. Routes to specialized Gomega skills.
Provides Go testing patterns including table-driven tests, subtests, benchmarks, fuzzing, and test coverage following TDD methodology.