samxiao
samxiao

Reputation: 2667

Test Golang Goroutine

I've been searching around, but so far only gone similar article written here by Ariejan de Vroom.

I would like to know if I can bring goroutine into unit testing such that it can precisely count the concurrent # of goroutines is running and can tell me if they are correctly spawned goroutine in the number I have stated.

I have the following code for example..

import (
    "testing"
    "github.com/stretchr/testify/assert"
)

func createList(job int, done chan bool) {
    time.Sleep(500)
    // do something
    time.Sleep(500)
    done <- true
    return
}

func TestNewList(t *testing.T) {
  list := NewList()
  if assert.NotNil(t, list) {
    const numGoRoutines = 16
    jobs := make(chan int, numGoRoutines)
    done := make(chan bool, 1)

    for j := 1; j <= numGoRoutines; j++ {
        jobs <- j
        go createList(j, done)
        fmt.Println("sent job", j)
    }
    close(jobs)
    fmt.Println("sent all jobs")
    <-done
}

Upvotes: 7

Views: 9927

Answers (2)

Sundrique
Sundrique

Reputation: 637

As I understood you are willing to limit the number of routines running simultaneously and verify whether it works properly. I would suggest to write a function which will take a routine as and argument and use mock routine to test it.
In the following example spawn function runs fn routines count times but no more than limit routines concurrently. I wrapped it into main function to run it at playground but you can use the same approach for your test method.

package main

import (
    "fmt"
    "sync"
    "time"
)

func spawn(fn func(), count int, limit int) {
    limiter := make(chan bool, limit)

    spawned := func() {
        defer func() { <-limiter }()
        fn()
    }

    for i := 0; i < count; i++ {
        limiter <- true
        go spawned()
    }
}

func main() {

    count := 10
    limit := 3

    var wg sync.WaitGroup
    wg.Add(count)

    concurrentCount := 0
    failed := false

    var mock = func() {
        defer func() {
            wg.Done()
            concurrentCount--
        }()

        concurrentCount++
        if concurrentCount > limit {
            failed = true // test could be failed here without waiting all routines finish
        }

        time.Sleep(100)
    }

    spawn(mock, count, limit)

    wg.Wait()

    if failed {
        fmt.Println("Test failed")
    } else {
        fmt.Println("Test passed")
    }
}

Playground

Upvotes: 1

VonC
VonC

Reputation: 1324278

One possible approch would be to use runtime.Stack() or analyze the output of runtime.debug.PrintStack() in order to see all goroutines at a given time.

Those options are detailed in "How to dump goroutine stacktraces?".

Upvotes: 0

Related Questions