Original post

Hey,

I’m building a web app and trying to learn unit test at the same time. I end up having alot of boiler plate in each test because of having to mock and inject dependencies alot. How could I write better test ?

Small example below :

func TestHandlerGetUser(t *testing.T) {
        service := new(mocks.Service)
        handler := user.NewHandler(service)
        e := echo.New()

        var u domain.User
        faker.FakeData(&u)

        t.Run("should return user with given id", func(t *testing.T) {
                req := httptest.NewRequest(http.MethodGet, "/", nil)
                rec := httptest.NewRecorder()
                c := e.NewContext(req, rec)
                service.On("GetByID", mock.Anything, u.ID).Return(u, nil).Once()
                c.SetPath("/api/users/:id")
                c.SetParamNames("id")
                c.SetParamValues(strconv.Itoa(u.ID))

                err := handler.GetUser(c)
                assert.NoError(t, err)
                assert.Equal(t, http.StatusOK, rec.Code)

                body, _ := ioutil.ReadAll(rec.Result().Body)
                var responseUser domain.User
                json.Unmarshal(body, &responseUser)
                assert.EqualValues(t, u, responseUser)
        })

        t.Run("should return bad request with wrong params", func(t *testing.T) {
                req := httptest.NewRequest(http.MethodGet, "/", nil)
                rec := httptest.NewRecorder()
                c := e.NewContext(req, rec)
                c.SetPath("/api/users/:id")
                c.SetParamNames("id")
                c.SetParamValues(u.Email)

                err := handler.GetUser(c)
                assert.NoError(t, err)
                assert.Equal(t, http.StatusBadRequest, rec.Code)
        })

        t.Run("should return not found", func(t *testing.T) {
                service.On("GetByID", mock.Anything, u.ID).Return(domain.User{}, sql.ErrNoRows).Once()
                req := httptest.NewRequest(http.MethodGet, "/", nil)
                rec := httptest.NewRecorder()
                c := e.NewContext(req, rec)
                c.SetPath("/api/users/:id")
                c.SetParamNames("id")
                c.SetParamValues(strconv.Itoa(u.ID))

                err := handler.GetUser(c)
                assert.NoError(t, err)
                assert.Equal(t, http.StatusNotFound, rec.Code)
        })

        t.Run("should return internal server error", func(t *testing.T) {
                service.On("GetByID", mock.Anything, u.ID).Return(domain.User{}, errors.New("fail")).Once()
                req := httptest.NewRequest(http.MethodGet, "/", nil)
                rec := httptest.NewRecorder()
                c := e.NewContext(req, rec)
                c.SetPath("/api/users/:id")
                c.SetParamNames("id")
                c.SetParamValues(strconv.Itoa(u.ID))

                err := handler.GetUser(c)
                assert.NoError(t, err)
                assert.Equal(t, http.StatusInternalServerError, rec.Code)
        })

}

My handler function is the following :

func (h *handler) GetUser(c echo.Context) error {
        ctx := c.Request().Context()
        userID, err := strconv.Atoi(c.Param("id"))
        if err != nil {
                return c.String(http.StatusBadRequest, "invalid request")
        }
        u, err := h.service.GetByID(ctx, userID)
        if err == sql.ErrNoRows {
                return c.String(http.StatusNotFound, "user not found")
        }
        if err != nil {
                return c.String(http.StatusInternalServerError, "unable to get user")
        }
        return c.JSON(http.StatusOK, u)
}