Run http.ListenAndServe() On Tests using stretchr/testify suite Stop Test From Proceed

Issue

I’m trying to create integration tests for my REST API application which I made using gorilla/mux, gorm.io and golang-migrate/v4

For the tests I’m using testify.

My SetupSuite() in my integration_Test.go is like this:

func (s *ReceiptServiceTestSuite) SetupSuite() {
    s.Require().NoError(godotenv.Load("test.env"))
    s.Require().NoError(database.Connect())
    s.db = database.DB
    s.m = database.M

    router.HandleRequests()
}

And my router.HandleRequests() is like this:

func HandleRequests() {
    router := mux.NewRouter()
    router.Use(middleware)
    // lots of router.HandleFunc()
    
    http.ListenAndServe(":8080", router)
}

The thing is: if I remove router.HandleRequests() from the SetupSuite(), all my database tests run normally, but if I try to http.ListenAndServe() the test workflow stop and nothing happens.

I believe that I should use goroutines with router.HandleRequests() so it can run parallel with the tests, I just can’t figure it out how to do it.

For further information, here is the project repository, and I don’t know if it is relevant, but I’m running two postgres instances using docker-compose, one for the project to run and another for the tests.

Solution

I figured out how to do it using go routines.

I read this example which teaches how to deal with os.Signals using channels and I implemented it this way:

First, I made the following changes at the router:

func HandleRequests() {
    router := mux.NewRouter()
    router.Use(middleware)
    // lots of router.HandleFunc()
    
    go http.ListenAndServe(":8080", router)

    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

    <-quit

}

So now http.ListenAndServe() is running in a new thread. I believe that by a default behavior, it stops at the end of the program execution.

After http.ListenAndServe(), I created a os.Signal chanel which will listen to incomming signals from the system (SIGINT and SIGTERM), then the function will stop at <-quit, which will be wating for one signal to continue.

Then I fixed my SetupSuite() to run router.HandleRequests() in a goroutine:

func (s *ReceiptServiceTestSuite) SetupSuite() {
    s.Require().NoError(godotenv.Load("test.env"))
    s.Require().NoError(database.Connect())
    s.db = database.DB
    s.m = database.M

    go router.HandleRequests()
}

And at my TearDownSuite() I send a SIGTERM signal to the current process, wich will be listened by the quit channel I’ve created before at router.HandleRequests() and the function will proceed to terminate the program.

func (s *ReceiptServiceTestSuite) TearDownSuite() {
    // some database code 
    p, _ := os.FindProcess(os.Getpid())
    p.Signal(syscall.SIGINT)
}

Answered By – Raphael Salomao

Answer Checked By – Clifford M. (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.