Reputation: 9
Here is how my basic structure of Todo app looks like called 'main.go';
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
// Todo Struct (Model)
type Todo struct {
Id string `json:"id"`
Task string `json:"task"`
Completed bool `json:"completed"`
}
var todos []Todo
// Get All Todos
func GetTodos(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(todos)
}
func main() {
// Init Router
r := mux.NewRouter()
// Mock Data
todos = append(todos, Todo{Id: "1", Task: "FirstTask", Completed: false})
todos = append(todos, Todo{Id: "2", Task: "SecondTask", Completed: false})
fmt.Println("Go dude dude go ")
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello dudes")
})
r.HandleFunc("/api/todos", GetTodos).Methods("GET")
log.Fatal(http.ListenAndServe(":8080", r))
}
And here is my 'main_test.go'
package main
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestGetTodos(t *testing.T) {
req, err := http.NewRequest("GET", "/api/todos", nil)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
handler := http.HandlerFunc(GetTodos)
handler.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}
// Check the response body is what we expect.
expected := `[{Id: "1", Task: "FirstTask", Completed: false},{Id: "2", Task: "SecondTask", Completed: false}]`
if rr.Body.String() != expected {
t.Errorf("handler returned unexpected body: got %v want %v",
rr.Body.String(), expected)
}
}
Problem is in here that I keep getting the following error;
--- FAIL: TestGetTodos (0.00s)
main_test.go:25: handler returned unexpected body: got null
want [{Id: "1", Task: "FirstTask", Completed: false},{Id: "2", Task: "SecondTask", Completed: false}]
FAIL
exit status 1
FAIL ..filesPath/backend 0.213s
I am definitely missing so simple but couldn't figure out.
Upvotes: 0
Views: 438
Reputation: 179
When you run your test runs, main is never called, so todos
is empty.
var todos []Todo
which is why rr.Body.String()
is returning null
If you move your mock data generation code into the GetTodos
function you are testing
var todos []Todo
// Get All Todos
func GetTodos(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var mockTodos []Todo
mockTodos = append(mockTodos, Todo{Id: "1", Task: "FirstTask", Completed: false})
mockTodos = append(mockTodos, Todo{Id: "2", Task: "SecondTask", Completed: false})
json.NewEncoder(w).Encode(mockTodos)
}
The test will still be failing, but the body will not be null anymore but instead show
=== RUN TestGetTodos
TestGetTodos: main_test.go:25: handler returned unexpected body: got [{"id":"1","task":"FirstTask","completed":false},{"id":"2","task":"SecondTask","completed":false}]
want [{Id: "1", Task: "FirstTask", Completed: false},{Id: "2", Task: "SecondTask", Completed: false}]
--- FAIL: TestGetTodos (0.00s)
FAIL
And that is because your expectation is not the correct json format. If you replace
expected := `[{Id: "1", Task: "FirstTask", Completed: false},{Id: "2", Task: "SecondTask", Completed: false}]`
with
expected := "[{\"id\":\"1\",\"task\":\"FirstTask\",\"completed\":false},{\"id\":\"2\",\"task\":\"SecondTask\",\"completed\":false}]\n"
That would help you pass the test.
I've added a newline \n
at the end of the expected string because json.NewEncoder(w).Encode(todos)
adds a newline to the output
Ideally, you do not want to use a global variable to store your state. You would have another object that would store the state or retrieve the state for you(possibly from a DB). In your test, you would initialize this object with your state and then check that your function performs correctly with this state as input.
Below is a simple example of what you could do as a next step.
main.go
// Todo Struct (Model)
type Todo struct {
Id string `json:"id"`
Task string `json:"task"`
Completed bool `json:"completed"`
}
type App struct {
todos []Todo
}
// Get All Todos
func (a *App) GetTodos(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(a.todos)
}
func main() {
// Init Router
r := mux.NewRouter()
// Mock Data
app := App{todos: []Todo{
{Id: "1", Task: "FirstTask", Completed: false},
{Id: "2", Task: "SecondTask", Completed: false},
}}
fmt.Println("Go dude dude go ")
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello dudes")
})
r.HandleFunc("/api/todos", app.GetTodos).Methods("GET")
log.Fatal(http.ListenAndServe(":8080", r))
}
main_test.go
func TestGetTodos(t *testing.T) {
req, err := http.NewRequest("GET", "/api/todos", nil)
if err != nil {
t.Fatal(err)
}
// Mock Data
app := App{todos: []Todo{
{Id: "1", Task: "FirstTask", Completed: false},
{Id: "2", Task: "SecondTask", Completed: false},
}}
rr := httptest.NewRecorder()
handler := http.HandlerFunc(app.GetTodos)
handler.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}
// Check the response body is what we expect.
expected := "[{\"id\":\"1\",\"task\":\"FirstTask\",\"completed\":false},{\"id\":\"2\",\"task\":\"SecondTask\",\"completed\":false}]\n"
str := rr.Body.String()
if diff := cmp.Diff(expected , str); diff != "" {
t.Errorf("%s: mismatch (-want +got):\n%s", "", diff)
}
if str != expected {
t.Errorf("handler returned unexpected body: got %v want %v",
rr.Body.String(), expected)
}
}
Upvotes: 1