I am having trouble trying to test this function with an error inside. The following is my ResponseJson function which does not return an error but sends a response json.
func ResponseJson(w http.ResponseWriter, Code int, Message string) {
jsonStatus := struct {
Code int `json:"code"`
Message string `json:"message"`
}{
Message: Message,
Code: Code,
}
bs, err := json.Marshal(jsonStatus);
if err != nil {
log.Println("Error in Marshal JSON in ResponseJson: ", err)
str := "Internal Server Error. Please contact the System Administrator."
io.WriteString(w, str);
return
} else {
io.WriteString(w, string(bs));
return
}
}
The following is my unit testing code which creates a mock ResponseWriter and it is able to successfully test the writer response json for cases with no errors. Since I am not returning an error type in ResponseJson() function, how do I test it inside the Test_ResponseJson function as shown below?
func Test_ResponseJson(t *testing.T) {
responseJsonTests := []struct {
testName string
code int
message string
expectedJsonResponse string
} {
{"Successful Login", http.StatusOK, "Successfully Logged In!", `{"code":200,"message":"Successfully Logged In!"}`},
{"Existing username", http.StatusBadRequest, "Username already exists. Please try again.", `{"code":400,"message":"Username already exists. Please try again."}`},
}
for _, e := range responseJsonTests {
// Creating a mock ResponseWriter
w := httptest.NewRecorder()
ResponseJson(w, e.code, e.message)
// Read the response body as a string
body, _ := io.ReadAll(w.Result().Body)
actual := string(body)
expected := e.expectedJsonResponse
if actual != expected {
t.Errorf("%s: expected %s but got %s", e.testName, e.expectedJsonResponse, actual)
}
}
}
Also, I have created a function which generates an actual log output for log.Println() built-in function. I am aware that the log.Println() function is a built-in function and it is highly unlikely to fail. However, I want to achieve 100% coverage in my unit testing.
func GenerateLogOutput(message string, errorMessage string) string {
// Create a new bytes.Buffer to capture the log output
var buf bytes.Buffer
// Redirect log output to a different destination set as a buffer
// By default, log message are written to the standard error stream os.Stderr
log.SetOutput(&buf)
// Generate an error
err := errors.New(errorMessage)
w := httptest.NewRecorder()
// Calling the function
InternalServerError(w, message, err)
actualOutput := buf.String()
return actualOutput
}
Simply, We can write a test case for the ResponseJson
function as below.
func Test_ResponseJson(t *testing.T) {
tests := []struct {
Code int
Message string
ExpectedStr string
}{
{
Code: 1,
Message: "sample message",
ExpectedStr: "{\"code\":1,\"message\":\"sample message\"}",
},
}
for _, test := range tests {
w := httptest.NewRecorder()
ResponseJson(w, test.Code, test.Message)
res := w.Result()
data, err := ioutil.ReadAll(res.Body)
res.Body.Close()
actualStr := string(data)
assert.Nil(t, err, "Invalid test data")
assert.Equal(t, actualStr, test.ExpectedStr)
}
}
We cannot get an error from bs, err := json.Marshal(jsonStatus)
. The json.Marshal
function can return two types of errors.
UnsupportedTypeError
(ex: channel, complex, and function values)UnsupportedValueError
(ex: cyclic data structures)We cannot parse values to generate either one of the above errors. We are parsing a struct with supported values and supported types. Therefore, we cannot write tests with 100% coverage.