英文:
mux.Vars is not able to retrieve var from httpTest request
问题
我正在为将来的项目编写一个简单的 REST 脚手架,目前我正在为我的控制器编写一些测试。我正在尝试通过 /todo/{id}
来获取一个待办事项,以下是处理程序的代码:
func (t TodoController) GetById(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id, err := strconv.Atoi(params["id"])
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
todo, err := t.todoService.GetById(id)
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
helpers.SendResponse(http.StatusOK, todo, w)
}
这是针对该控制器的测试代码:
var (
todoController TodoController
recorder *httptest.ResponseRecorder
todos []models.Todo
)
func setup() {
todoController = *NewTodoController(services.NewTodoService())
recorder = httptest.NewRecorder()
todos = []models.Todo{
{
ID: 0,
Todo: "购买牛奶",
Completed: false,
},
{
ID: 1,
Todo: "购买奶酪",
Completed: false,
},
{
ID: 2,
Todo: "购买鸡蛋",
Completed: false,
},
}
}
func TestGetById(t *testing.T) {
// 准备
setup()
request := httptest.NewRequest(http.MethodGet, "/todo/1", nil)
var response models.Todo
// 执行
todoController.GetById(recorder, request)
result := recorder.Result()
defer result.Body.Close()
data, err := ioutil.ReadAll(result.Body)
err = json.Unmarshal(data, &response)
// 断言
if err != nil {
t.Errorf("期望的错误为 nil,但得到了 %v", err)
}
assert.Equal(t, result.StatusCode, http.StatusOK, "响应应该是 200 OK")
assert.Equal(t, response, todos[1], "响应与预期结果不匹配")
}
看起来当发送请求到 /todo/1
时,mux 无法获取到 Id,因此返回了一个 BadRequest 错误。
这是该存储库的链接:https://github.com/Je12emy/rest_boiler
英文:
I'm writting a simple rest boiler plate for future projects, I'm currently working on some tests for my controller, I'm trying to retrieve a todo by it's Id at /todo/{id}
, here's the handler.
func (t TodoController) GetById(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id, err := strconv.Atoi(params["id"])
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
todo, err := t.todoService.GetById(id)
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
helpers.SendResponse(http.StatusOK, todo, w)
}
And here's the test for this controller.
var (
todoController TodoController
recorder *httptest.ResponseRecorder
todos []models.Todo
)
func setup() {
todoController = *NewTodoController(services.NewTodoService())
recorder = httptest.NewRecorder()
todos = []models.Todo{
{
ID: 0,
Todo: "Buy milk",
Completed: false,
},
{
ID: 1,
Todo: "Buy cheese",
Completed: false,
},
{
ID: 2,
Todo: "Buy eggs",
Completed: false,
},
}
}
func TestGetById(t *testing.T) {
// Arrange
setup()
request := httptest.NewRequest(http.MethodGet, "/todo/1", nil)
var response models.Todo
// Act
todoController.GetById(recorder, request)
result := recorder.Result()
defer result.Body.Close()
data, err := ioutil.ReadAll(result.Body)
err = json.Unmarshal(data, &response)
// Assert
if err != nil {
t.Errorf("Expected error to be nil but got %v", err)
}
assert.Equal(t, result.StatusCode, http.StatusOK, "Response should have been 200 Ok")
assert.Equal(t, response, todos[1], "Response did not match the expected result")
}
It looks like when sending a request to /todo/1
mux is not able to retrieve the Id, so it end up returning a BadRequest error.
Here's a link to this repo: https://github.com/Je12emy/rest_boiler
答案1
得分: 1
Mux提供了两种注入请求参数的方法 - SetURLVars
助手和使用mux.Router
包装器而不是直接调用处理程序。
SetURLVars正是你想要的 - 将本来无法访问的参数注入到HTTP请求中。
这种方法很简单,但有一个问题。使用的URL和注入的参数不同步。
没有禁止开发人员编写这样的代码:
// 我们在请求路径中使用2
request := httptest.NewRequest(http.MethodGet, "/todo/2", nil)
// 我们在请求参数中使用1
request = SetURLVars(request, map[string]string{"id":"1"})
这不是很干净的测试实践。
我们没有测试路由中的变量名是否正确。我们可以在路由器中使用item_id
而不是id
,测试不会捕捉到这个错误。
不仅如此,我们还可以在路由器定义中使用错误的路径,并映射到不同的处理程序。如果我们犯了这个错误,客户端可能会删除order
而不是todo
项目。
如果我们在测试中使用我们的生产路由器,这个问题可以解决。
假设这是我们的生产代码:
func InitRouter(t TodoController) http.handler
r := mux.NewRouter()
r.HandleFunc("/todo/{id}", t.GetById).Methods(http.MethodGet)
return r
在测试中,我们可以通过在InitRouter
函数中创建的路由器来测试GetById
:
func TestGetById(t *testing.T) {
// 准备
setup()
request := httptest.NewRequest(http.MethodGet, "/todo/1", nil)
var response models.Todo
// 添加准备
sut := InitRouter(todoController)
// 执行
// 更改执行 - 通过生产路由器调用GetById
sut.ServeHTTP(recorder, request)
// 之后没有更改
result := recorder.Result()
defer result.Body.Close()
...
}
英文:
Mux provides two ways to inject request params - SetURLVars
helper and using mux.Router
wrapper instead of direct handler call.
SetURLVars does exactly what you want - injects otherwise not accessible parameters into HTTP request.
This method is simple, but has one issue though. Used URL and injected parameters are out of sync.
None forbids developers to write this code:
// we use 2 in request path
request := httptest.NewRequest(http.MethodGet, "/todo/2", nil)
// and we use 1 in request param
request = SetURLVars(request, map[string]string{"id":"1"})
This is not very clean testing practice.
We do not test if our var names in routing is correct. We can use item_id
in router instead of id
and test does not catch that.
Not only that, but we can use wrong path in router definition and map different handler. Client can delete order
instead of todo
item if we make that mistake.
That could be solved if we use our production Router in test.
Assume it is our production code:
func InitRouter(t TodoController) http.handler
r := mux.NewRouter()
r.HandleFunc("/todo/{id}", t.GetById).Methods(http.MethodGet)
return r
In test, we can test GetById
through router we created in InitRouter
function:
func TestGetById(t *testing.T) {
// Arrange
setup()
request := httptest.NewRequest(http.MethodGet, "/todo/1", nil)
var response models.Todo
// added setup
sut := InitRouter(todoController)
// Act
// changed act - calling GetById through production router
sut.ServeHTTP(recorder, request)
// no chnages after that
result := recorder.Result()
defer result.Body.Close()
...
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论