httptest ResponseRecorder 保留了旧值。

huangapple go评论94阅读模式
英文:

httptest ResponseRecorder keeps the old value

问题

我有一个需要测试的handlerAuthentication函数:

func handlerAuthentication(c *gin.Context) {
	session := Session.GetSession(c)
	var login Login
	err := c.BindJSON(&login)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	client, err := initClient(c, login)
	fmt.Println("Error: ", err)
	if err != nil {
		fmt.Println("There's an error!")
		c.JSON(http.StatusUnauthorized, gin.H{"error": ErrorWrongLogin})
		return
	}
	err = (*client).Logout(c)
	if err != nil {
		return
	}

	session.Set("username", login.Username)
	session.Set("password", login.Password)
	err = session.Save()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "an error occurred during the save of the session:" + err.Error()})
		return
	}
	c.JSON(http.StatusOK, "Connected")
}

为了测试它,我做了以下工作:

func TestHandlerAuthentication(t *testing.T) {
	UrlOdoo = "https://isi.nc"
	resp := httptest.NewRecorder()
	gin.SetMode(gin.TestMode)
	c, r := gin.CreateTestContext(resp)

	r.POST("/test", func(c *gin.Context) {
		handlerAuthentication(c)
	})

	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	Odoo = OdooRPC{createMockOdooClient}
	client = mock_odoorpc.NewMockOdooClient(ctrl)
	client.EXPECT().Authenticate(gomock.Any(), gomock.Any(), invalidUsername, invalidPassword).AnyTimes().Return(fmt.Errorf("invalid login"))
	client.EXPECT().Authenticate(gomock.Any(), gomock.Any(), validUsername, validPassword).AnyTimes().Return(nil)
	client.EXPECT().Logout(gomock.Any()).AnyTimes().Return(nil)

	session = mock_session.NewMockSession(ctrl)
	Session = SessionGetter{createMockSession}
	session.EXPECT().Set("username", validUsername).AnyTimes().Return()
	session.EXPECT().Set("password", validPassword).AnyTimes().Return()
	session.EXPECT().Save().AnyTimes().Return(nil)

	for name, test := range map[string]struct {
		input Login
		want  int
	}{
		"valid login": {
			input: Login{
				Username: validUsername,
				Password: validPassword,
			},
			want: 200,
		},
		"invalid login": {
			input: Login{
				Username: invalidUsername,
				Password: invalidPassword,
			},
			want: 401,
		},
	} {
		t.Run(name, func(t *testing.T) {
			body, _ := json.Marshal(test.input)
			c.Request, _ = http.NewRequest(http.MethodPost, "/test", strings.NewReader(string(body)))
			r.ServeHTTP(resp, c.Request)
			assert.Equal(t, test.want, resp.Code)
			resp.Flush()
		})
	}
}

我面临的问题是,如果我逐个运行测试(valid logininvalid login),它们都会通过,但是当我同时运行这两个测试时,第二个测试会失败。
以下是同时执行这两个测试的示例:

=== RUN   TestHandlerAuthentication
=== RUN   TestHandlerAuthentication/valid_login
Error: <nil> //没有错误,所以resp.Code应该等于200
=== RUN   TestHandlerAuthentication/invalid_login
Error: invalid login //有错误,所以resp.Code应该等于401
There's an error!
main_test.go:394: 
Error Trace:    main_test.go:394
Error:          Not equal: 
expected: 401
actual  : 200
Test:           TestHandlerAuthentication/invalid_login
--- FAIL: TestHandlerAuthentication (0.00s)
--- PASS: TestHandlerAuthentication/valid_login (0.00s)
--- FAIL: TestHandlerAuthentication/invalid_login (0.00s)
Expected :401
Actual   :200

正如预期的那样,当登录无效时发生错误,但是resp.Code仍然是200。
如果我先运行invalid login测试,resp.Code仍然是401。

这是因为测试是并行执行的,而httptestResponseRecorder在并行中不起作用吗?

谢谢你的帮助。

英文:

I have a handlerAuthentication function that I need to test:

func handlerAuthentication(c *gin.Context) {
	session := Session.GetSession(c)
	var login Login
	err := c.BindJSON(&amp;login)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{&quot;error&quot;: err.Error()})
		return
	}
	client, err := initClient(c, login)
	fmt.Println(&quot;Error: &quot;,err)
	if err != nil {
		fmt.Println(&quot;There&#39;s an error !&quot;)
		c.JSON(http.StatusUnauthorized, gin.H{&quot;error&quot;: ErrorWrongLogin})
		return
	}
	err = (*client).Logout(c)
	if err != nil {
		return
	}

	session.Set(&quot;username&quot;, login.Username)
	session.Set(&quot;password&quot;, login.Password)
	err = session.Save()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{&quot;error&quot;: &quot;an error occurred during the save of the session:&quot; + err.Error()})
		return
	}
	c.JSON(http.StatusOK, &quot;Connected&quot;)
}

To do so,I made this:

func TestHandlerAuthentication(t *testing.T) {
	UrlOdoo = &quot;https://isi.nc&quot;
	resp := httptest.NewRecorder()
	gin.SetMode(gin.TestMode)
	c, r := gin.CreateTestContext(resp)

	r.POST(&quot;/test&quot;, func(c *gin.Context) {
		handlerAuthentication(c)
	})

	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	Odoo = OdooRPC{createMockOdooClient}
	client = mock_odoorpc.NewMockOdooClient(ctrl)
	client.EXPECT().Authenticate(gomock.Any(), gomock.Any(), invalidUsername, invalidPassword).AnyTimes().Return(fmt.Errorf(&quot;invalid login&quot;))
	client.EXPECT().Authenticate(gomock.Any(), gomock.Any(), validUsername, validPassword).AnyTimes().Return(nil)
	client.EXPECT().Logout(gomock.Any()).AnyTimes().Return(nil)

	session = mock_session.NewMockSession(ctrl)
	Session = SessionGetter{createMockSession}
	session.EXPECT().Set(&quot;username&quot;, validUsername).AnyTimes().Return()
	session.EXPECT().Set(&quot;password&quot;, validPassword).AnyTimes().Return()
	session.EXPECT().Save().AnyTimes().Return(nil)

	for name, test := range map[string]struct {
		input   Login
		want    int
	}{
		&quot;valid login&quot;: {
			input: Login{
				Username: validUsername,
				Password: validPassword,
			},
			want: 200,
		},
		&quot;invalid login&quot;: {
			input: Login{
				Username: invalidUsername,
				Password: invalidPassword,
			},
			want: 401,
		},
	} {
		t.Run(name, func(t *testing.T) {
			body, _ := json.Marshal(test.input)
			c.Request, _ = http.NewRequest(http.MethodPost, &quot;/test&quot;, strings.NewReader(string(body)))
			r.ServeHTTP(resp, c.Request)
			assert.Equal(t, test.want, resp.Code)
			resp.Flush()
		})
	}
}

The problem I'm facing is that if I do the tests one by (valid login and invalid login), they all pass, but when I do the two tests at the same time, the second test fails.
Here's an exemple of execution of the two tests at the same time:

=== RUN   TestHandlerAuthentication
=== RUN   TestHandlerAuthentication/valid_login
Error:  &lt;nil&gt; //No error, so resp.Code should be equal to 200
=== RUN   TestHandlerAuthentication/invalid_login
Error:  invalid login //Error, so resp.Code should be equal to 401
There&#39;s an error !
main_test.go:394: 
Error Trace:	main_test.go:394
Error:      	Not equal: 
expected: 401
actual  : 200
Test:       	TestHandlerAuthentication/invalid_login
--- FAIL: TestHandlerAuthentication (0.00s)
--- PASS: TestHandlerAuthentication/valid_login (0.00s)
--- FAIL: TestHandlerAuthentication/invalid_login (0.00s)
Expected :401
Actual   :200

As expected, an error occured when the login is invalid, but the resp.Code is still 200.
And if I do the invalid login test first, the resp.Code will still be 401.

Is it happening because the tests are parallelized and the httptest ResponseRecorder doesn't work in parallel ?

Thank you for your help.

答案1

得分: 0

谢谢leaf bebop

我需要为每个测试初始化一个新的httptest.ResponseRecorder。为此,我将初始化移到了t.Run(name,func(t *testing.T)函数中:

func TestHandlerAuthentication(t *testing.T) {
	UrlOdoo = "https://isi.nc"

	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	Odoo = OdooRPC{createMockOdooClient}
	client = mock_odoorpc.NewMockOdooClient(ctrl)
	client.EXPECT().Authenticate(gomock.Any(), gomock.Any(), invalidUsername, invalidPassword).AnyTimes().Return(fmt.Errorf("invalid login"))
	client.EXPECT().Authenticate(gomock.Any(), gomock.Any(), validUsername, validPassword).AnyTimes().Return(nil)
	client.EXPECT().Logout(gomock.Any()).AnyTimes().Return(nil)

	session = mock_session.NewMockSession(ctrl)
	Session = SessionGetter{createMockSession}
	session.EXPECT().Set("username", validUsername).AnyTimes().Return()
	session.EXPECT().Set("password", validPassword).AnyTimes().Return()
	session.EXPECT().Save().AnyTimes().Return(nil)

	for name, test := range map[string]struct {
		input Login
		want  int
	}{
		"valid login": {
			input: Login{
				Username: validUsername,
				Password: validPassword,
			},
			want: 200,
		},
		"invalid login": {
			input: Login{
				Username: invalidUsername,
				Password: invalidPassword,
			},
			want: 401,
		},
	} {
		t.Run(name, func(t *testing.T) {
			resp := httptest.NewRecorder()
			gin.SetMode(gin.TestMode)
			c, r := gin.CreateTestContext(resp)

			r.POST("/test", func(c *gin.Context) {
				handlerAuthentication(c)
			})

			body, _ := json.Marshal(test.input)
			c.Request, _ = http.NewRequest(http.MethodPost, "/test", strings.NewReader(string(body)))
			r.ServeHTTP(resp, c.Request)
			assert.Equal(t, test.want, resp.Code)
		})
	}
}
英文:

Thank you leaf bebop

I needed to initialize a new httptest.ResponseRecorder for each test.
To do so, I move the initialisation to the t.Run(name,func(t *testing.T) function:

func TestHandlerAuthentication(t *testing.T) {
	UrlOdoo = &quot;https://isi.nc&quot;

	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	Odoo = OdooRPC{createMockOdooClient}
	client = mock_odoorpc.NewMockOdooClient(ctrl)
	client.EXPECT().Authenticate(gomock.Any(), gomock.Any(), invalidUsername, invalidPassword).AnyTimes().Return(fmt.Errorf(&quot;invalid login&quot;))
	client.EXPECT().Authenticate(gomock.Any(), gomock.Any(), validUsername, validPassword).AnyTimes().Return(nil)
	client.EXPECT().Logout(gomock.Any()).AnyTimes().Return(nil)

	session = mock_session.NewMockSession(ctrl)
	Session = SessionGetter{createMockSession}
	session.EXPECT().Set(&quot;username&quot;, validUsername).AnyTimes().Return()
	session.EXPECT().Set(&quot;password&quot;, validPassword).AnyTimes().Return()
	session.EXPECT().Save().AnyTimes().Return(nil)

	for name, test := range map[string]struct {
		input Login
		want  int
	}{
		&quot;valid login&quot;: {
			input: Login{
				Username: validUsername,
				Password: validPassword,
			},
			want: 200,
		},
		&quot;invalid login&quot;: {
			input: Login{
				Username: invalidUsername,
				Password: invalidPassword,
			},
			want: 401,
		},
	} {
		t.Run(name, func(t *testing.T) {
			resp := httptest.NewRecorder()
			gin.SetMode(gin.TestMode)
			c, r := gin.CreateTestContext(resp)

			r.POST(&quot;/test&quot;, func(c *gin.Context) {
				handlerAuthentication(c)
			})

			body, _ := json.Marshal(test.input)
			c.Request, _ = http.NewRequest(http.MethodPost, &quot;/test&quot;, strings.NewReader(string(body)))
			r.ServeHTTP(resp, c.Request)
			assert.Equal(t, test.want, resp.Code)
		})
	}
}

huangapple
  • 本文由 发表于 2021年6月29日 10:06:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/68171765.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定