gosubmit: Parse HTML forms and build pre-filled requests for submitting
While writing the tests for rondoBB, I realized I was writing too much code to test simple form submissions. In fact, it was so verbose that it took a really long time to write these tests and I writing them. Since there is a CSRF token on every form, I had to extract it them from the GET
request response and fill it in manually to make the test to work. There are also some <input type="hidden">
fields whose values are filled on the server-side. Even though I had helper functions written for this, it was still a pain to use them.
I first discovered the amazing goquery
library, but it didn't help reduce the amount of code so I realized I had to write the right tool for the job: gosubmit.
gosubmit
uses x/net/html
to automatically parse the HTML from a *http.Response
body and create a list of all found <form>
elements in the document. Additionally, it parses all input values on the form so the user doesn't need to fill the values manually that are already there.
Below is an usage example. Let's say we'd like to test a handler that looks like this:
1package app 2 3import ( 4 "net/http" 5) 6 7func Serve(w http.ResponseWriter, r *http.Request) { 8 switch r.Method { 9 case http.MethodPost: 10 username := r.FormValue("username") 11 password := r.FormValue("password") 12 csrf := r.FormValue("csrf") 13 if csrf == "1234" && username == "user" && password == "pass" { 14 w.Write([]byte("Welcome, " + username)) 15 return 16 } 17 w.WriteHeader(http.StatusForbidden) 18 default: 19 w.Write([]byte(`<!DOCTYPE html> 20<html> 21<body> 22<form name="test" method="POST"> 23 <input type="text" name="username"> 24 <input type="password" name="password"> 25 <input type="hidden" name="csrf" value="1234"> 26 <input type="submit"> 27</form>`)) 28 } 29} 30 31var mux *http.ServeMux 32 33func init() { 34 mux = http.NewServeMux() 35 mux.HandleFunc("/auth/login", Serve) 36}
Our test can look like:
1package app 2 3import ( 4 "net/http/http" 5 "net/http/httptest" 6 "testing" 7 8 . "github.com/jeremija/gosubmit" 9) 10 11func TestLogin(t *testing.T) { 12 w := httptest.NewRecorder() 13 r := httptest.NewRequest("GET", "/auth/login", nil) 14 mux.ServeHTTP(w, r) 15 16 r = ParseResponse(w.Result(), r.URL). 17 FirstForm(). 18 Testing(t). 19 NewTestRequest( 20 Set("username", "user"), 21 Set("password", "pass"), 22 ) 23 w = httptest.NewRecorder() 24 mux.ServeHTTP(w, r) 25 26 if w.Code != http.StatusOK { 27 t.Fatalf("Expected status %d, but got %d", http.StatusOK, w.Code) 28 } 29}
Note that there was no need to call Set("csrf", "1234")
- gosubmit
already found the value of it by parsing the GET
request.
For more information, checkout the gosubmit source code.