Firstly, there is no need to use any external mocking library like:
All you really is need is just an interface and some piece of code using standard library to run all your tests in parallel.
Check this real world example below instead of next "calculator testing example":
├── api
│ ├── storage.go
│ ├── server.go
│ └── server_test.go
└── main.go
api/storage.go
package api
import "database/sql"
type storage struct {
// any database driver of your choice...
pool *sql.DB
}
func NewStorage(p *sql.DB) *storage {
return &storage{pool: p}
}
func (s *storage) Find() (string, error) {
// use database driver to find from storage...
return `{ "id": "123" }`, nil
}
func (s *storage) Add(v string) error {
// use database driver to add to storage...
return nil
}
api/server.go
package api
import (
"fmt"
"net/http"
)
type Storage interface {
Find() (string, error)
Add(string) error
}
type server struct {
storage Storage
}
func NewServer(s Storage) *server {
return &server{storage: s}
}
func (s *server) Find(w http.ResponseWriter, r *http.Request) {
response, err := s.storage.Find()
if err != nil {
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, `{ "message": "not found" }`)
return
}
fmt.Fprint(w, response)
}
func (s *server) Add(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
name := query.Get("name")
if err := s.storage.Add(name); err != nil {
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, `{ "message": "not found" }`)
return
}
fmt.Fprint(w, `{ "message": "success" }`)
}
api/server_test.go
package api
import (
"errors"
"net/http"
"net/http/httptest"
"testing"
)
type mock struct {
find func() (string, error)
add func(string) error
}
func (m *mock) Find() (string, error) { return m.find() }
func (m *mock) Add(v string) error { return m.add(v) }
func TestFindOk(t *testing.T) {
t.Parallel()
// Arrange
expectedBody := `{ "message": "ok" }`
expectedStatus := http.StatusOK
m := &mock{find: func() (string, error) { return expectedBody, nil }}
server := NewServer(m)
recorder := httptest.NewRecorder()
// Act
server.Find(recorder, &http.Request{})
// Assert
if recorder.Code != expectedStatus {
t.Errorf("want %d, got %d", expectedStatus, recorder.Code)
}
if recorder.Body.String() != expectedBody {
t.Errorf("want %s, got %s", expectedBody, recorder.Body.String())
}
}
func TestFindNotFound(t *testing.T) {
t.Parallel()
// Arrange
expectedBody := `{ "message": "not found" }`
expectedStatus := http.StatusNotFound
m := &mock{find: func() (string, error) { return expectedBody, errors.New("not found") }}
server := NewServer(m)
recorder := httptest.NewRecorder()
// Act
server.Find(recorder, &http.Request{})
// Assert
if recorder.Code != expectedStatus {
t.Errorf("want %d, got %d", expectedStatus, recorder.Code)
}
if recorder.Body.String() != expectedBody {
t.Errorf("want %s, got %s", expectedBody, recorder.Body.String())
}
}
func TestAddOk(t *testing.T) {
t.Parallel()
// Arrange
expectedBody := `{ "message": "success" }`
expectedStatus := http.StatusOK
m := &mock{add: func(string) error { return nil }}
server := NewServer(m)
recorder := httptest.NewRecorder()
// Act
request, _ := http.NewRequest("GET", "/add?name=mike", nil)
server.Add(recorder, request)
// Assert
if recorder.Code != expectedStatus {
t.Errorf("want %d, got %d", expectedStatus, recorder.Code)
}
if recorder.Body.String() != expectedBody {
t.Errorf("want %s, got %s", expectedBody, recorder.Body.String())
}
}
Run your tests
go clean -testcache
go test ./...