Web App Testing – POST Testing

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.

Sharing is caring:

Leave a Comment