📖 5 min read (~ 1100 words).

Safety

Checks Against Leaked Resources (Goroutines, File Descriptors)

Assertions

GoDoc

All links point to https://pkg.go.dev/github.com/go-openapi/testify/v2

This domain exposes 2 functionalities.

NoFileDescriptorLeak

NoFileDescriptorLeak ensures that no file descriptor leaks from inside the tested function.

This assertion works on Linux only (via /proc/self/fd). On other platforms, the test is skipped.

NOTE: this assertion is not compatible with parallel tests. File descriptors are a process-wide resource; concurrent tests opening files would cause false positives.

Sockets, pipes, and anonymous inodes are filtered out by default, as these are typically managed by the Go runtime.

Concurrency

NoFileDescriptorLeak is not compatible with parallel tests. File descriptors are a process-wide resource; any concurrent I/O from other goroutines may cause false positives.

Calls to NoFileDescriptorLeak are serialized with a mutex to prevent multiple leak checks from interfering with each other.

Examples
	NoFileDescriptorLeak(t, func() {
		// code that should not leak file descriptors
	})
	success: func() {}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNoFileDescriptorLeak(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"runtime"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	if runtime.GOOS != "linux" {
    		// This example is only runnable on linux. On other platforms, the assertion skips the test.
    		// We force the expected output below, so that tests don't fail on other platforms.
    		fmt.Println("success: true")
    
    		return
    	}
    
    	t := new(testing.T) // should come from testing, e.g. func TestNoFileDescriptorLeak(t *testing.T)
    	success := assert.NoFileDescriptorLeak(t, func() {
    	})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNoFileDescriptorLeak(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"runtime"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	if runtime.GOOS != "linux" {
    		// This example is only runnable on linux. On other platforms, the assertion skips the test.
    		// We force the expected output below, so that tests don't fail on other platforms.
    		fmt.Println("passed")
    
    		return
    	}
    
    	t := new(testing.T) // should come from testing, e.g. func TestNoFileDescriptorLeak(t *testing.T)
    	require.NoFileDescriptorLeak(t, func() {
    	})
    	fmt.Println("passed")
    
    }

NoGoRoutineLeak

NoGoRoutineLeak ensures that no goroutine did leak from inside the tested function.

NOTE: only the go routines spawned from inside the tested function are checked for leaks. No filter or configuration is needed to exclude “known go routines”.

Resource cleanup should be done inside the tested function, and not using testing.T.Cleanup, as t.Cleanup is called after the leak check.

Edge cases

  • if the tested function panics leaving behind leaked goroutines, these are detected.
  • if the tested function calls runtime.Goexit (e.g. from testing.T.FailNow) leaving behind leaked goroutines, these are detected.
  • if a panic occurs in one of the leaked go routines, it cannot be recovered with certainty and the calling program will usually panic.

Concurrency

NoGoRoutineLeak may be used safely in parallel tests.

Examples
	NoGoRoutineLeak(t, func() {
		...
	},
	"should not leak any go routine",
	)
	success: func() {}
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNoGoRoutineLeak(t *testing.T)
    	success := assert.NoGoRoutineLeak(t, func() {
    	})
    	fmt.Printf("success: %t\n", success)
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"sync"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/assert"
    )
    
    func main() {
    	t := new(testing.T) // normally provided by test
    
    	blocker := make(chan struct{})
    	var wg sync.WaitGroup
    
    	defer func() {
    		// clean resources _after_ the test
    		close(blocker)
    		wg.Wait()
    	}()
    
    	wg.Add(1)
    	// This examplifies how a function that leaks a goroutine is detected.
    	result := assert.NoGoRoutineLeak(t, func() { // true when there is no leak
    		go func() {
    			defer wg.Done()
    			<-blocker // leaked: blocks until cleanup
    		}()
    	})
    
    	// Error message from test would typically return the leaked goroutine, e.g.:
    	// #	0x69c8e8	github.com/go-openapi/testify/v2/assert_test.ExampleNoGoRoutineLeak.func2.1+0x48	.../assert_adhoc_example_7_test.go:30
    	fmt.Printf("has leak: %t", !result)
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T)
    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(testing.T) // should come from testing, e.g. func TestNoGoRoutineLeak(t *testing.T)
    	require.NoGoRoutineLeak(t, func() {
    	})
    	fmt.Println("passed")
    
    }
  • Copy and click to open Go Playground

    // real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T)
    // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers
    // SPDX-License-Identifier: Apache-2.0
    
    package main
    
    import (
    	"fmt"
    	"sync"
    
    	"github.com/go-openapi/testify/v2/require"
    )
    
    func main() {
    	t := new(mockFailNowT) // normally provided by test
    	// Since this test is failing and calls [runtime.Goexit], we need a mock to
    	// avoid the example trigger a panick.
    
    	blocker := make(chan struct{})
    	var wg sync.WaitGroup
    
    	defer func() {
    		// clean resources _after_ the test
    		close(blocker)
    		wg.Wait()
    	}()
    
    	wg.Add(1)
    	// This examplifies how a function that leaks a goroutine is detected.
    	require.NoGoRoutineLeak(t, func() { // true when there is no leak
    		go func() {
    			defer wg.Done()
    			<-blocker // leaked: blocks until cleanup
    		}()
    	})
    
    	// Error message from test would typically return the leaked goroutine, e.g.:
    	// #	0x69c8e8	github.com/go-openapi/testify/v2/assert_test.ExampleNoGoRoutineLeak.func2.1+0x48	.../assert_adhoc_example_7_test.go:30
    	fmt.Printf("passed: %t", !t.Failed())
    
    }
    
    type mockFailNowT struct {
    	failed bool
    }
    
    // Helper is like [testing.T.Helper] but does nothing.
    func (mockFailNowT) Helper() {}
    
    func (m *mockFailNowT) Errorf(format string, args ...any) {
    	_ = format
    	_ = args
    }
    
    func (m *mockFailNowT) FailNow() {
    	m.failed = true
    }
    
    func (m *mockFailNowT) Failed() bool {
    	return m.failed
    }


Generated with github.com/go-openapi/testify/codegen/v2