In this article, we are going to test our REST API by using the HTTP test library and we are going to use SQLite 3 as the database.
Remember on a previous article we applied a clean architecture approach to build a Restful API, where we created two endpoints, one to create new and one in order to get existing posts. And this approach allowed us to swap between different database, we can use Firestore, MySQL, SQLite… And we can swap between different HTTP frameworks, in this case we are able to use CHI or we can use MUX or any other HTTP frameworks . And this approach also enables a better the stability of our application so we created a test in our business logic, so we mocked the repository and we were able to isolate the business layer to test it.
SQLite PostRepository implementation
We are going to use SQLite so we need a new repository implementation of SQLite for the PostRepository interface
type PostRepository interface {
Save(post *entity.Post) (*entity.Post, error)
FindAll() ([]entity.Post, error)
Delete(post *entity.Post) error
}
First we need to install SQLite library
$ go get -u github.com/mattn/go-sqlite3
Then we create the SQLiteRepository which is the implementation of SQL for the PostRepository interface in the file repository/sqlite-repo.go
package repository
import (
"database/sql"
"log"
"os"
"golang-mux-api/entity"
_ "github.com/mattn/go-sqlite3"
)
type sqliteRepo struct{}
func NewSQLiteRepository() PostRepository {
os.Remove("./posts.db")
db, err := sql.Open("sqlite3", "./posts.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
sqlStmt := `
create table posts (id integer not null primary key, title text, txt text);
delete from posts;
`
_, err = db.Exec(sqlStmt)
if err != nil {
log.Printf("%q: %s\n", err, sqlStmt)
}
return &sqliteRepo{}
}
func (*sqliteRepo) Save(post *entity.Post) (*entity.Post, error) {
db, err := sql.Open("sqlite3", "./posts.db")
if err != nil {
log.Fatal(err)
return nil, err
}
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
return nil, err
}
stmt, err := tx.Prepare("insert into posts(id, title, txt) values(?, ?, ?)")
if err != nil {
log.Fatal(err)
return nil, err
}
defer stmt.Close()
_, err = stmt.Exec(post.ID, post.Title, post.Text)
if err != nil {
log.Fatal(err)
return nil, err
}
tx.Commit()
return post, nil
}
func (*sqliteRepo) FindAll() ([]entity.Post, error) {
db, err := sql.Open("sqlite3", "./posts.db")
if err != nil {
log.Fatal(err)
return nil, err
}
rows, err := db.Query("select id, title, txt from posts")
if err != nil {
log.Fatal(err)
return nil, err
}
defer rows.Close()
var posts []entity.Post
for rows.Next() {
var id int64
var title string
var text string
err = rows.Scan(&id, &title, &text)
if err != nil {
log.Fatal(err)
return nil, err
}
post := entity.Post{
ID: id,
Title: title,
Text: text,
}
posts = append(posts, post)
}
err = rows.Err()
if err != nil {
log.Fatal(err)
return nil, err
}
return posts, nil
}
func (*sqliteRepo) Delete(post *entity.Post) error {
db, err := sql.Open("sqlite3", "./posts.db")
if err != nil {
log.Fatal(err)
return err
}
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
return err
}
stmt, err := tx.Prepare("delete from posts where id = ?")
if err != nil {
log.Fatal(err)
return err
}
defer stmt.Close()
_, err = stmt.Exec(post.ID)
if err != nil {
log.Fatal(err)
return err
}
tx.Commit()
return nil
}
Run and verify SQLite implementation
To start the REST API
$ go run *.go
Mux HTTP server running on port :8000
Let’s try to create some new posts
curl -v --location 'http://localhost:8000/posts' \
--header 'Content-Type: application/json' \
--data '{
"title": "Title 1",
"text": "Text 1"
}'
{"id": 8936326456275005323,"title": "Title 1","text": "Text 1"
}
$ curl --location 'http://localhost:8000/posts' \
--header 'Content-Type: application/json' \
--data '{
"title": "Title 2",
"text": "Text 2"
}'
{"id":8802839608357740770,"title":"Title 2","text":"Text 2"}
You can verify the results by openning the SQLite database from the file ./posts.db

You can also verify the results by calling the REST API to get all existing posts
$ curl -v --location 'http://localhost:8000/posts'
* Trying 127.0.0.1:8000...
* Connected to localhost (::1) port 8000 (#0)
> GET /posts HTTP/1.1
> Host: localhost:8000
< HTTP/1.1 200 OK
< Content-Type: application/json
< Content-Length: 124
[{"id":8802839608357740770,"title":"Title 2","text":"Text 2"},{"id":8936326456275005323,"title":"Title 1","text":"Text 1"}]
Create test for the controller
We are going to create new test for our controller, by creating new file called post-controller_test.go and we are going to use the HTTP test library and SQLite as database, and we are going to create basically two tests TestAddPost and TestGetPosts
package controller
import (
"bytes"
"encoding/json"
"golang-rest-api/entity"
"golang-rest-api/repository"
"golang-rest-api/service"
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
const (
ID int64 = 123
TITLE string = "Title 1"
TEXT string = "Text 1"
)
var (
postRepo repository.PostRepository = repository.NewSQLiteRepository()
postSrv service.PostService = service.NewPostService(postRepo)
postController PostController = NewPostController(postSrv)
)
func TestAddPost(t *testing.T) {
// Create a new HTTP POST request
var jsonReq = []byte(`{"title": "Title 1","text": "Text 1"}`)
req, _ := http.NewRequest("POST", "/posts", bytes.NewBuffer(jsonReq))
// Assign HTTP Handler function (controller AddPost function)
handler := http.HandlerFunc(postController.AddPost)
// Record HTTP Response (httptest)
response := httptest.NewRecorder()
// Dispatch the HTTP request
handler.ServeHTTP(response, req)
// Add Assertions on the HTTP Status code and the response
status := response.Code
if status != http.StatusOK {
t.Errorf("Handler returnd a wrong status code: got %v want %v", status, http.StatusOK)
}
// Decode the HTTP response
var post entity.Post
json.NewDecoder(io.Reader(response.Body)).Decode(&post)
// Assert HTTP response
assert.NotNil(t, post.ID)
assert.Equal(t, TITLE, post.Title)
assert.Equal(t, TEXT, post.Text)
// Clean up database
cleanUp(&post)
}
func TestGetPosts(t *testing.T) {
// Insert new post
setup()
// Create a GET HTTP request
req, _ := http.NewRequest("GET", "/posts", nil)
// Assign HTTP Handler function (controller GetPosts function)
handler := http.HandlerFunc(postController.GetPosts)
// Record HTTP Response (httptest)
response := httptest.NewRecorder()
// Dispatch the HTTP request
handler.ServeHTTP(response, req)
// Add Assertions on the HTTP Status code and the response
status := response.Code
if status != http.StatusOK {
t.Errorf("Handler returnd a wrong status code: got %v want %v", status, http.StatusOK)
}
// Decode the HTTP response
var posts []entity.Post
json.NewDecoder(io.Reader(response.Body)).Decode(&posts)
// Assert HTTP response
assert.NotNil(t, posts[0].ID)
assert.Equal(t, TITLE, posts[0].Title)
assert.Equal(t, TEXT, posts[0].Text)
// Clean up database
cleanUp(&posts[0])
}
func cleanUp(post *entity.Post) {
postRepo.Delete(post)
}
func setup() {
var post entity.Post = entity.Post{
ID: ID,
Title: TITLE,
Text: TEXT,
}
postRepo.Save(&post)
}
Run and verify test cases
First run the TestAddPost test case
$ cd controller/
$ go test -v -run TestAddPost
=== RUN TestAddPost
--- PASS: TestAddPost (0.01s)
PASS
ok golang-mux-api/controller 0.031s
Then run the TestGetPosts test case
$ go test -v -run TestGetPosts
=== RUN TestGetPosts
--- PASS: TestGetPosts (0.01s)
PASS
ok golang-mux-api/controller 0.037s
Download Source Code
$ git clone https://github.com/favtuts/golang-mux-api.git
$ cd golang-mux-api$ git checkout rest-api-testing
$ go build$ go run .