I am trying to unit test an API which returns a CSV response. I am used to testing APIs that return Json response by doing something like this :
resBody := `{"access_token": "token","instance_url": "url","id": "id","token_type": "Bearer"}`
r := ioutil.NopCloser(bytes.NewReader([]byte(resBody)))
clientMock.GetMock = func(url string) (*http.Response, error) {
return &http.Response{
StatusCode: 200,
Body: r,
}, nil
}
Here, the resBody is my mocked Json response, however unable to figure out what would be an equivalent response structure for testing APIs that return csv response.
Never mind, able to figure it out now.
Below can be used to mock CSV response :
resBody := `"access_token","instance_url","id","token_type"` + "\n" + `"token","url","id","Bearer"`
r := ioutil.NopCloser(bytes.NewReader([]byte(resBody)))
clientMock.GetMock = func(url string) (*http.Response, error) {
return &http.Response{
StatusCode: 200,
Body: r,
}, nil
}
Related
I am writing a RESTAPI in Go, using Mux and GORM, I have been able to generate the token and then return it, but I am struggling to find a way to update the global variable set in Postman.
I am able to add the token to the Authorization header, but once I send another request, it gets replaced with null defeating the purpose of JWT authentication.
Here is the test I've got in Postman
var jsonData = JSON.parse(responseBody);
if (jsonData["token"] === null) {
// ...
} else {
postman.setGlobalVariable("token", jsonData["token"]);
}
As you can see I tried to run an if statement to essentially do nothing if there isn't a token, but it doesn't seem to work, the authorization header keeps getting set to null
Here is the handler in Go
func LoginHandler(w http.ResponseWriter, r *http.Request) {
var user User
db.DB.Where("email = ?", r.FormValue("email")).Find(&user)
w.Header().Set("Content-Type", "application/json")
if user.checkPassword(r.FormValue("password")) {
token, err := user.generateJWT()
w.Header().Set("Authorization", token.Token)
if err != nil {
customHTTP.NewErrorResponse(w, http.StatusUnauthorized, "Error: "+err.Error())
return
}
json.NewEncoder(w).Encode(&token)
} else {
fmt.Println(user)
customHTTP.NewErrorResponse(w, http.StatusUnauthorized, "Password incorrect")
return
}
}
and here is the function that generates the JWT token:
func (u User) generateJWT() (JWTToken, error) {
signingKey := []byte(os.Getenv("JWT_SECRET"))
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"exp": time.Now().Add(time.Hour * 1 * 1).Unix(),
"user_id": int(u.ID),
"name": u.Name,
"email": u.Email,
})
tokenString, err := token.SignedString(signingKey)
return JWTToken{tokenString}, err
}
Also here is the JWTMiddleWare function:
func JWTMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tokenString := r.Header.Get("Authorization")
if len(tokenString) == 0 {
customHTTP.NewErrorResponse(w, http.StatusUnauthorized, "Authentication failure")
return
}
tokenString = strings.Replace(tokenString, "Bearer ", "", 1)
claims, err := VerifyToken(tokenString)
if err != nil {
customHTTP.NewErrorResponse(w, http.StatusUnauthorized, "Error verifying JWT token: "+err.Error())
return
}
userId := strconv.FormatFloat(claims.(jwt.MapClaims)["user_id"].(float64), 'g', 1, 64)
r.Header.Set("userId", userId)
next.ServeHTTP(w, r)
})
}
I am essentially just looking for a way to store the token in the authentication header so that it can be cross referenced later in the code when trying to visit a protected handler.
I have the following code:
// HTTPPost to post json messages to the specified url
func HTTPPost(message interface{}, url string) (*http.Response, error) {
jsonValue, err := json.Marshal(message)
if err != nil {
logger.Error("Cannot Convert to JSON: ", err)
return nil, err
}
logger.Info("Calling http post with url: ", url)
resp, err := getClient().Post(url, "application/json", bytes.NewBuffer(jsonValue))
if err != nil {
logger.Error("Cannot post to the url: ", url, err)
return nil, err
}
err = IsErrorResp(resp, url)
return resp, err
}
I'd like to write the tests for this, but I am not sure how to use httptest package .
Take a look here:
https://golang.org/pkg/net/http/httptest/#example_Server
Basically, you can create a new "mock" http server using httptest.NewServer function.
You can have your mock server return whatever response you need from the test, and you can also have your mock server store the request that your HTTPPost function made in order to assert over it.
func TestYourHTTPPost(t *testing.T){
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, `response from the mock server goes here`)
// you can also inspect the contents of r (the request) to assert over it
}))
defer ts.Close()
mockServerURL = ts.URL
message := "the message you want to test"
resp, err := HTTPPost(message, mockServerURL)
// assert over resp and err here
}
I am stuck over testing with mocking, Here is my route for handler:
r.Handle("/users/{userID}", negroni.New(
negroni.HandlerFunc(validateTokenMiddleware),
negroni.Wrap(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
getUserDetailsHandler(w, r, db)
})),
)).Methods("GET")
And here is my handler:
func getUserDetailsHandler(w http.ResponseWriter, r *http.Request, db *sql.DB) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
//Create UserDetailsView instance
var userview UserDetailsView
//Get varibale from mux
vars := mux.Vars(r)
//UserID fetches userId from vars
userID := vars["userID"]
//Get user Information by wpUsersID
wuis := store.NewWpUserInformationStore(db)
userInformation, _:= wuis.GetByID(uID)
json.NewEncoder(w).Encode(userview);
//Print result
w.WriteHeader(http.StatusOK)
}
And i mock the function which is in store package named as GetByID which is looks like this :
type wpUserInfoMockStore struct {
mock.Mock
}
func (m *wpUserInfoMockStore) GetByID(user *WpUserInformation) error {
rets := m.Called(user)
return rets.Error(0)
}
//InitMockStore store
func InitMockStore() *wpUserInfoMockStore {
s := new(wpUserInfoMockStore)
//store = s
return s
}
And i write test case for handler but i got an error cannot convert getUserDetailsHandler (type func(http.ResponseWriter, *http.Request, *sql.DB)) to type http.HandlerFunc but i can not find why is it happened, here i'm using reference for this https://github.com/sohamkamani/blog_example__go_web_db and here is my test case code:
func TestGetUserDetailsTes(t *testing.T) {
// Initialize the mock store
mockStore := store.InitMockStore()
mockStore.On("GetByID").Return([]*store.WpUserInformation{{
21,
sql.NullString{String: "john"},
sql.NullString{String: "Sorensen"},
0}}, nil).Once()
req, err := http.NewRequest("GET", "", nil)
//if requests gives error
if err != nil {
panic(err.Error())
}
//parameters for generateTestUserJWT are set
testUser.ID = "22"
testUser.UserName = "johns"
testUser.Depot = "NYC"
//JWT generated
refToken, err := generateTestJWT(testUser, false)
//handling error while generating token
if err != nil {
panic(err.Error())
}
//token returned is concatenated with Bearer string
newToken = "Bearer " + refToken
//request authorization header is set
req.Header.Set("Authorization", newToken)
req.Header.Set("Latitude", "123.12")
req.Header.Set("Longitude", "456.45")
//response is set
w := httptest.NewRecorder()
hf := http.HandlerFunc(getUserDetailsHandler)
hf.ServeHTTP(w, req)
//if response code is not statusOK then test fails
if w.Code != http.StatusOK {
t.Errorf("/users/{userID} GET request failed, got: %d, want: %d.", w.Code, http.StatusOK)
}
}
As you see i test handler without url like req, err := http.NewRequest("GET", "", nil) but when i used link inside then i can not able to use mock functions, here what am i missing/fault please help me out.
Thank you.
Use a middleware handler for generating the function. Pass a handler in your main function which will call your middleware returning http.handler. That way you can pass db object to your main data and which will call the middle ware returning handler.
func getUserDetailsHandler(w http.ResponseWriter, r *http.Request, db *sql.DB) http.HandlerFunc{
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
//Create UserDetailsView instance
var userview UserDetailsView
//Get varibale from mux
vars := mux.Vars(r)
//UserID fetches userId from vars
userID := vars["userID"]
//Get user Information by wpUsersID
wuis := store.NewWpUserInformationStore(db)
userInformation, _:= wuis.GetByID(uID)
json.NewEncoder(w).Encode(userview);
//Print result
w.WriteHeader(http.StatusOK)
}
}
I am trying to mock a multiple http requests using gock for unit testing but I am getting this error:
Error: Get https://192.64.23.33/q1/status: gock: cannot match any request
Error: Get https://194.55.5.6/q1/status: gock: cannot match any request
Code:
func GetEmployee(url string, cli *http.Client) *Response {
//Do something
return Response
}
func company(url []string, cli *http.Client) []string {
for employee := range url {
resultList := GetEmployee(employee, cli)
//Process and return an array
return resultList
}
}
Unittest:
func TestCompany(t *Testing.t){
Convey("testing function company",t,func() {
expected := &Response: {
Name: "xyz",
ID: 1",
Status: "active",
}
MockCli := &http.Client{}
testemployees := []string{"192.64.23.33","194.55.5.6"}
for _, testempl := range testemployees {
gock.New(fmt.Sprintf("https://%s", testempl)).
Get("/q1/status").
Reply(200).
JSON(expected)
gock.InterceptClient(MockCli)
response, err = company(testemployees, MockCli)
//some assertions
}
})
}
Unit tests failed when I am passing testemployees := []string{"192.64.23.33","194.55.5.6"} more than one element in a list otherwise if there is only one element then it passes as expected.
How to mock a multiple URLs here so that gock can match a request?
I am using this Alamofire request and returning a json
let todoEndpoint: String = "https://api.abc.com/api/v3/products?pid=uid8225&format=json&&offset=0&limit=10"
Alamofire.request(todoEndpoint)
.responseJSON { response in
guard let json = response.result.value as? [String: Any] else {
print("didn't get todo object as JSON from API")
print("Error: \(response.result.error)")
return
}
print(json)
}
Now i have do loop where i want to use this json value but i am getting:
error : Use of unresolved identifier json ?
do {
for (index,subJson):(String, JSON) in json {
print("Index :\(index) Title: \(subJson)" )
} catch
{
print("there was an error")
}
As mentioned :
https://github.com/Alamofire/Alamofire#response-string-handler
" the result of a request is only available inside the scope of a response closure. Any execution contingent on the response or data received from the server must be done within a response closure."
How can i use this json value outside scope of response closure ?
Can you please suggest
Is there any completion handler i need to write and how can it be done ?
Thanks
Better use the SwiftyJson for easy json parsing with Almofire like bellow :
import Alamofire
import SwiftyJSON
static func getRequest(urlString: URL?, Parameter:NSDictionary?, completion: #escaping (_ serverResponse: AnyObject?,_ error:NSError?)->()){
Alamofire.request(urlString!, parameters:nil, headers: nil).responseJSON { response in
if(response.result.value != nil){
let serverResponse = JSON(response.result.value!)
print("Array value is \(serverResponse.arrayValue)")
completion(serverResponse as AnyObject?, nil)
}
else{
completion(nil, response.result.error as NSError?)
}
}
}
You can write this function in a separate class like NetworkLayer and then call it from any class for the WebService call as given bellow :
let url = NSURL(string: yourWebServiceUrlString)
NetworkLayer.getRequest(urlString: url as URL?, Parameter: nil) { (serverResponse, error) in
if (error == nil){
print("Server response: \(serverResponse)")
}
}
Note: You can also pass the parameter dictionary in it.