Setelah persiapan pada modul sebelumnya kita lakukan, pada modul ini kita akan membuat fungsi test untuk POST pada fungsi Login().
Pertama kita tambahkan database connection pada cmd/web/setup_test.go
package main
import (
"log"
"os"
"testing"
"webapp/pkg/db"
)
var app application
func TestMain(m *testing.M) {
templatePath = "./../../templates/"
app.Session = getSession() //tambahkan session pada modul test
app.DSN = "host=localhost port=5432 user=postgres password=postgres dbname=users sslmode=disable timezone=UTC connect_timeout=5"
conn, err := app.connectToDB()
if err != nil {
log.Fatal(err)
}
defer conn.Close()
app.DB = db.PostgresConn{DB: conn}
os.Exit(m.Run())
}
Kemudian Buka file cmd/web/handlers_test.go, tambahkan fungsi Test_app_Login() seperti berikut:
package main
import (
"context"
"io"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
)
func Test_application_handlers(t *testing.T) {
var theTests = []struct {
name string
url string
expectedStatusCode int
}{
{"home", "/", http.StatusOK},
{"404", "/qwerty", http.StatusNotFound},
}
routes := app.routes()
//create a test server
ts := httptest.NewTLSServer(routes)
defer ts.Close()
//testing
for _, e := range theTests {
resp, err := ts.Client().Get(ts.URL + e.url)
if err != nil {
t.Log(err)
t.Fatal(err)
}
if resp.StatusCode != e.expectedStatusCode {
t.Errorf("for %s: expected status %d, but got %d", e.name, e.expectedStatusCode, resp.StatusCode)
}
}
}
func TestAppHome(t *testing.T) {
var tests = []struct {
name string
putInSession string
expctedHTML string
}{
{"First Visit", "", "<small>Session:"},
{"Second Visit", "skillplus", "<small>Session: skillplus"},
}
for _, e := range tests {
req, _ := http.NewRequest("GET", "/", nil)
req = addCtxSessToReq(req, app)
_ = app.Session.Destroy(req.Context())
if e.putInSession != "" {
app.Session.Put(req.Context(), "test", e.putInSession)
}
rr := httptest.NewRecorder()
handler := http.HandlerFunc(app.Home)
handler.ServeHTTP(rr, req)
//check status code
if rr.Code != http.StatusOK {
t.Errorf("TestAppHome: returned wrong status code, expected 200 but got %d", rr.Code)
}
body, _ := io.ReadAll(rr.Body)
if !strings.Contains(string(body), e.expctedHTML) {
t.Errorf("%s: did not find %s in response body", e.name, e.expctedHTML)
}
}
}
func TestApp_renderWBadTemplate(t *testing.T) {
templatePath = "./testdata/"
req, _ := http.NewRequest("GET", "/", nil)
req = addCtxSessToReq(req, app)
rr := httptest.NewRecorder()
err := app.render(rr, req, "bad.page.gohtml", &TemplateData{})
if err == nil {
t.Error("Expected error from bad template, but did not")
}
templatePath = "./../../templates/"
}
func addCtxSessToReq(req *http.Request, app application) *http.Request {
req = req.WithContext(context.WithValue(req.Context(), contextUserKey, "unknown"))
ctx, _ := app.Session.Load(req.Context(), req.Header.Get("X-Session"))
return req.WithContext(ctx)
}
func Test_app_Login(t *testing.T) {
var tests = []struct {
name string
postedData url.Values
expectedStatCode int
expectedLoc string
}{
{
name: "Valid Login",
postedData: url.Values{"email": {"admin@example.com"}, "password": {"secret"}},
expectedStatCode: http.StatusSeeOther,
expectedLoc: "/user/profile",
},
{
name: "Missing form data",
postedData: url.Values{"email": {""}, "password": {""}},
expectedStatCode: http.StatusSeeOther,
expectedLoc: "/",
},
{
name: "User not found",
postedData: url.Values{"email": {"me@skillplus.web.id"}, "password": {"123"}},
expectedStatCode: http.StatusSeeOther,
expectedLoc: "/",
},
{
name: "Bad Credentials",
postedData: url.Values{"email": {"admin@example.com"}, "password": {"123"}},
expectedStatCode: http.StatusSeeOther,
expectedLoc: "/",
},
}
for _, e := range tests {
req, _ := http.NewRequest("POST", "/login", strings.NewReader(e.postedData.Encode()))
req = addCtxSessToReq(req, app)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
rr := httptest.NewRecorder()
handler := http.HandlerFunc(app.Login)
handler.ServeHTTP(rr, req)
if rr.Code != e.expectedStatCode {
t.Errorf("%s: returned wrong stat code: expected %d, but got %d", e.name, e.expectedStatCode, rr.Code)
}
actualLoc, err := rr.Result().Location()
if err == nil {
if actualLoc.String() != e.expectedLoc {
t.Errorf("%s: expected location %s, but got %s", e.name, e.expectedLoc, actualLoc.String())
}
} else {
t.Errorf("%s: no location header set", e.name)
}
}
}
Jika kita jalankan test, sesuai ekspektasi, test berhasil.
cmd/web$ go test -v -run Test_app_Login
2022/10/25 17:23:21 Connected to Postgres!
=== RUN Test_app_Login
--- PASS: Test_app_Login (1.90s)
PASS
ok webapp/cmd/web 2.475s
Sampai disini kita sudah menambahkan testing untuk fungsi Login yang menggunakan POST method.
Pada modul selanjutnya kita akan menggunakan authentication diatas dalam middleware, untuk memastikan halaman tertentu diakses oleh authenticated user. Setelah middleware dibuat, kita juga akan buat fungsi testingnya.