When building a great application, it is a good idea to plan for proper testing of each of the components that make the application tick. Through unit testing, we can test each function within the application as a separate entity, to make sure it performs based on our testing criteria.
But how do you do this in a Golang application? What if you need to test endpoints that were created using the Gorilla mux package?
We’re going to see how to develop unit tests for our functions as well as HTTP endpoints in a Golang application using the available Go testing framework.
We’re going to break down this guide into two different parts. First we’re going to explore some simple test scenarios and work our way into testing API endpoints that are served with HTTP and the Gorilla mux routing package.
To create unit tests in the Go programming language, you need to creating testing files with a _test suffix. For example, main_test.go would be a valid test file. The prefix of these files don’t need to match to corresponding files to the test files, but it is good practice. For example, if you have a file called main.go it would be a good idea to have a test file called main_test.go.
Create and open a $GOPATH/src/github.com/nraboy/testproject/main.go file and include the following code that we’re going to soon test against:
package main
func Add(value1 int, value2 int) int {
return value1 + value2
}
func Subtract(value1 int, value2 int) int {
return value1 - value2
}
func main() { }
The functions in the above code are incredibly simple, but it still allows us to prove our point. We are going to create a unit test for each of these two functions.
Create and open a $GOPATH/src/github.com/nraboy/testproject/main_test.go file and include the following test code:
package main
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestAdd(t *testing.T) {
total := Add(1, 3)
assert.NotNil(t, total, "The `total` should not be `nil`")
assert.Equal(t, 4, total, "Expecting `4`")
}
func TestSubtract(t *testing.T) {
total := Subtract(1, 3)
assert.NotNil(t, total, "The `total` should not be `nil`")
assert.Equal(t, -2, total, "Expecting `-2`")
}
While we have the ability to write tests out of the box with Golang, if we want some useful tools such as assert
, we’ll need to grab a package off the internet. To install the testify package, execute the following:
go get github.com/stretchr/testify/assert
Notice that in our test file we have two methods for each of the corresponding methods that we wish to test. These tests have a very important naming convention. Each test that you wish to have must start with Test
, so for example, TestXxx
will be a valid test.
To run these tests, execute the following:
go test
The command above will run tests in all _test.go files and all TestXxx
methods. Play around with the assertions to see what kind of results you’ll get.
There are a lot of things you can do when it comes to testing. If you need to initialize variables or anything else prior to running your tests, create an init
function and it will be executed first.
So what happens if you need to test API endpoints? These are a little different because they need to be tested as HTTP requests rather than functions.
Take the following very unimpressive and simple API:
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
)
func CreateEndpoint(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(200)
w.Write([]byte("Item Created"))
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/create", CreateEndpoint).Methods("GET")
log.Fatal(http.ListenAndServe(":12345", router))
}
If you serve this application you have a single API endpoint that is accessible via a GET request at http://localhost:12345/create. You can certainly test this simple endpoint by hand, but let’s check out what unit testing would look like.
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
)
func Router() *mux.Router {
router := mux.NewRouter()
router.HandleFunc("/create", CreateEndpoint).Methods("GET")
return router
}
func TestCreateEndpoint(t *testing.T) {
request, _ := http.NewRequest("GET", "/create", nil)
response := httptest.NewRecorder()
Router().ServeHTTP(response, request)
assert.Equal(t, 200, response.Code, "OK response is expected")
}
In the above test file we create a function for initializing our application router. Just like in the core code, both use the router that is part of the Gorilla mux package. This test file has a single test function to test our single API endpoint.
Our goal is to fire off an HTTP request to the /create
endpoint. Using the ServeHTTP
function we can pass our request to the specific route and store the response for analysis. Our test is to make sure we receive a status 200. Of course you could have a much more complicated test that checks exact data as well.
What if we need to pass a POST body via our HTTP requests for testing? Let’s assume the CreateEndpoint
is a POST endpoint that expects a JSON body.
func TestCreate(t *testing.T) {
person := &Person{
Firstname: "Nic",
Lastname: "Raboy"
}
jsonPerson, _ := json.Marshal(person)
request, _ := http.NewRequest("POST", "/create", bytes.NewBuffer(jsonPerson))
response := httptest.NewRecorder()
Router().ServeHTTP(response, request)
assert.Equal(t, 200, response.Code, "OK response is expected")
}
In the above test method we have a fictional data structure called Person
, and it doesn’t really matter what it holds. The point here is that we’re marshaling it into JSON and adding it to the third parameter of the NewRequest
function.
You just saw some ways to unit test your Golang application. My examples were very simple, but we saw how to create basic tests for functions as well as basic tests for HTTP endpoints. It is a great idea to include tests in your application to help eliminate possible errors at release. If you’re using Atom or another compatible IDE, you can download plugins that will automatically run your tests when you hit the save button.
A video version of this article can below.