The gbytes package provides utilities for testing byte streams, buffers, and I/O operations. It includes a thread-safe Buffer type and matchers for testing streaming content.
import "github.com/onsi/gomega/gbytes"Thread-safe buffer for testing I/O operations. Implements io.Writer and io.Reader with additional test helpers.
type Buffer struct {
// Contains filtered or unexported fields
}The Buffer type provides a thread-safe wrapper around a byte buffer optimized for testing scenarios where you need to:
Creates a new empty buffer.
func NewBuffer() *BufferReturns: *Buffer - New empty buffer
Usage:
buffer := gbytes.NewBuffer()Creates a buffer pre-filled with content.
func BufferWithBytes(bytes []byte) *BufferParameters:
bytes: Initial contentReturns: *Buffer - Buffer with initial content
Usage:
buffer := gbytes.BufferWithBytes([]byte("initial content\n"))Creates a buffer that wraps an io.Reader.
func BufferReader(reader io.Reader) *BufferParameters:
reader: io.Reader to wrapReturns: *Buffer - Buffer that reads from the provided reader
Usage:
file, _ := os.Open("data.txt")
buffer := gbytes.BufferReader(file)Writes data to the buffer. Implements io.Writer.
func (buf *Buffer) Write(p []byte) (n int, err error)Parameters:
p: Bytes to writeReturns:
n: Number of bytes writtenerr: Error (always nil for Buffer)Usage:
buffer := gbytes.NewBuffer()
cmd.Stdout = buffer
cmd.Run()Reads data from the buffer. Implements io.Reader.
func (buf *Buffer) Read(p []byte) (n int, err error)Parameters:
p: Byte slice to read intoReturns:
n: Number of bytes readerr: Error (io.EOF when empty)Usage:
data := make([]byte, 1024)
n, err := buffer.Read(data)Returns all content currently in the buffer.
func (buf *Buffer) Contents() []byteReturns: []byte - Buffer contents
Usage:
buffer := gbytes.NewBuffer()
fmt.Fprintf(buffer, "test output")
content := buffer.Contents()
Expect(content).To(Equal([]byte("test output")))Clears all content from the buffer.
func (buf *Buffer) Clear() errorReturns: error - Always returns nil
Usage:
err := buffer.Clear()
Expect(err).NotTo(HaveOccurred())
Expect(buffer.Contents()).To(BeEmpty())Closes the buffer. After closing, the buffer can no longer be written to.
func (buf *Buffer) Close() errorReturns: error - Always nil
Usage:
buffer.Close()Returns whether the buffer has been closed.
func (buf *Buffer) Closed() boolReturns: bool - True if buffer is closed
Usage:
if buffer.Closed() {
t.Log("Buffer has been closed")
}Creates a channel that receives true when the buffer contains data matching the specified pattern.
func (buf *Buffer) Detect(desired string, args ...any) chan boolParameters:
desired: Regular expression pattern (or format string with args)args: Optional format argumentsReturns: chan bool - Channel that receives true when pattern is detected
Usage:
buffer := gbytes.NewBuffer()
detected := buffer.Detect("SUCCESS")
go func() {
fmt.Fprintln(buffer, "Operation SUCCESS")
}()
Eventually(detected).Should(Receive(BeTrue()))Cancels all pending Detect operations and cleans up their goroutines.
func (buf *Buffer) CancelDetects()Usage:
buffer := gbytes.NewBuffer()
_ = buffer.Detect("pattern1")
_ = buffer.Detect("pattern2")
// Cancel all pending detections
buffer.CancelDetects()Returns the buffer itself, implementing the BufferProvider interface.
func (buf *Buffer) Buffer() *BufferReturns: *Buffer - The buffer itself
Usage:
// Useful when Buffer implements BufferProvider interface
var provider gbytes.BufferProvider = buffer
buf := provider.Buffer()Method version of Say matcher for use with Eventually/Consistently.
func (buf *Buffer) Say(expected any, args ...any) types.GomegaMatcherParameters:
expected: String pattern or matcherargs: Format arguments if expected contains format specifiersReturns: Matcher that succeeds when buffer contains expected content
Usage:
buffer := gbytes.NewBuffer()
cmd.Stdout = buffer
go cmd.Run()
Eventually(buffer).Should(gbytes.Say("expected output"))Like Say but with custom timeout for Eventually.
func (buf *Buffer) SayWithTimeout(expected any, timeout time.Duration, args ...any) types.GomegaMatcherParameters:
expected: String pattern or matchertimeout: Custom timeout durationargs: Format argumentsReturns: Matcher with custom timeout
Usage:
Eventually(buffer).Should(buffer.SayWithTimeout("ready", 5*time.Second))Matcher that succeeds when buffer contains expected content. Useful with Eventually for streaming output.
func Say(expected any, args ...any) types.GomegaMatcherParameters:
expected: Can be:
args: Format arguments if expected contains format specifiersReturns: types.GomegaMatcher that matches buffer contents
Usage:
// Wait for specific output
buffer := gbytes.NewBuffer()
cmd.Stdout = buffer
go cmd.Run()
Eventually(buffer).Should(gbytes.Say("Server started"))
// Match multiple patterns
Eventually(buffer).Should(gbytes.Say("Status: %s", "OK"))
// Use with Consistently to ensure output doesn't appear
Consistently(buffer).ShouldNot(gbytes.Say("ERROR"))
// Match with regex
Eventually(buffer).Should(gbytes.Say(`\d+ requests processed`))Advanced Usage:
// Combine with other matchers
Eventually(buffer).Should(gbytes.Say(ContainSubstring("success")))
// Check ordered output
Eventually(buffer).Should(gbytes.Say("Phase 1"))
Eventually(buffer).Should(gbytes.Say("Phase 2"))
Eventually(buffer).Should(gbytes.Say("Phase 3"))
// Verify multiple independent outputs
buffer1 := gbytes.NewBuffer()
buffer2 := gbytes.NewBuffer()
cmd.Stdout = buffer1
cmd.Stderr = buffer2
Eventually(buffer1).Should(gbytes.Say("stdout message"))
Eventually(buffer2).Should(gbytes.Say("stderr message"))Interface for types that can provide a Buffer.
type BufferProvider interface {
Buffer() *Buffer
}Types implementing this interface can be used directly with the Say matcher. The Buffer type itself implements this interface.
Example:
var provider gbytes.BufferProvider = gbytes.NewBuffer()
buffer := provider.Buffer()Wraps an io.Closer with timeout enforcement.
func TimeoutCloser(c io.Closer, timeout time.Duration) io.CloserParameters:
c: io.Closer to wraptimeout: Maximum duration to wait for Close() to completeReturns: io.Closer that enforces timeout
Example:
file, _ := os.Open("data.txt")
closer := gbytes.TimeoutCloser(file, 5*time.Second)
err := closer.Close()
if err == gbytes.ErrTimeout {
log.Println("Close operation timed out")
}Wraps an io.Reader with timeout enforcement.
func TimeoutReader(r io.Reader, timeout time.Duration) io.ReaderParameters:
r: io.Reader to wraptimeout: Maximum duration to wait for Read() to completeReturns: io.Reader that enforces timeout
Example:
reader := gbytes.TimeoutReader(slowReader, 2*time.Second)
data := make([]byte, 1024)
n, err := reader.Read(data)
if err == gbytes.ErrTimeout {
log.Println("Read operation timed out")
}Wraps an io.Writer with timeout enforcement.
func TimeoutWriter(w io.Writer, timeout time.Duration) io.WriterParameters:
w: io.Writer to wraptimeout: Maximum duration to wait for Write() to completeReturns: io.Writer that enforces timeout
Example:
writer := gbytes.TimeoutWriter(slowWriter, 3*time.Second)
n, err := writer.Write([]byte("data"))
if err == gbytes.ErrTimeout {
log.Println("Write operation timed out")
}Error returned when a timeout operation exceeds its duration.
var ErrTimeout errorUsage:
reader := gbytes.TimeoutReader(slowReader, 1*time.Second)
_, err := reader.Read(data)
if errors.Is(err, gbytes.ErrTimeout) {
log.Println("Operation timed out")
}buffer := gbytes.NewBuffer()
cmd := exec.Command("myapp", "args")
cmd.Stdout = buffer
cmd.Stderr = buffer
err := cmd.Start()
Expect(err).ShouldNot(HaveOccurred())
// Wait for specific output
Eventually(buffer, 5*time.Second).Should(gbytes.Say("Application started"))
// Verify no errors appeared
Consistently(buffer, 2*time.Second).ShouldNot(gbytes.Say("ERROR|FATAL"))
cmd.Wait()buffer := gbytes.NewBuffer()
go func() {
stream.WriteTo(buffer)
}()
// Verify data appears in order
Eventually(buffer).Should(gbytes.Say("Header"))
Eventually(buffer).Should(gbytes.Say("Data chunk 1"))
Eventually(buffer).Should(gbytes.Say("Data chunk 2"))
Eventually(buffer).Should(gbytes.Say("Footer"))var logBuffer *gbytes.Buffer
BeforeEach(func() {
logBuffer = gbytes.NewBuffer()
logger.SetOutput(logBuffer)
})
It("logs important events", func() {
service.DoSomething()
Eventually(logBuffer).Should(gbytes.Say("INFO.*Operation started"))
Eventually(logBuffer).Should(gbytes.Say("INFO.*Operation completed"))
})
It("doesn't log sensitive data", func() {
service.ProcessData(sensitiveData)
Consistently(logBuffer).ShouldNot(gbytes.Say("password|secret|token"))
})buffer := gbytes.NewBuffer()
cmd.Stdout = buffer
go cmd.Run()
// Match patterns across lines
Eventually(buffer).Should(gbytes.Say("Connection established"))
Eventually(buffer).Should(gbytes.Say("Authenticated"))
Eventually(buffer).Should(gbytes.Say("Ready"))
// Verify final state
Eventually(buffer).Should(gbytes.Say("Shutdown complete"))
Expect(buffer.Contents()).To(ContainSubstring("No errors"))stdout := gbytes.NewBuffer()
stderr := gbytes.NewBuffer()
cmd.Stdout = stdout
cmd.Stderr = stderr
go cmd.Run()
// Verify normal output on stdout
Eventually(stdout).Should(gbytes.Say("Processing..."))
Eventually(stdout).Should(gbytes.Say("Done"))
// Verify warnings on stderr
Eventually(stderr).Should(gbytes.Say("Warning: deprecated API"))
// Ensure no errors
Consistently(stderr).ShouldNot(gbytes.Say("Error|Failed"))Buffer is thread-safe. Multiple goroutines can safely:
This makes it ideal for testing concurrent systems where output may be produced by multiple goroutines.
buffer := gbytes.NewBuffer()
// Safe: multiple writers
go func() { fmt.Fprintln(buffer, "Writer 1") }()
go func() { fmt.Fprintln(buffer, "Writer 2") }()
go func() { fmt.Fprintln(buffer, "Writer 3") }()
// Safe: concurrent reading
Eventually(buffer).Should(gbytes.Say("Writer 1"))
Eventually(buffer).Should(gbytes.Say("Writer 2"))
Eventually(buffer).Should(gbytes.Say("Writer 3"))