[英]What is the idiomatic way to test wrapper functions in Golang?
It is not unusual, when we have a function with a lot of parameters, to create some companion-functions that use the general function with pre-defined parameters.这并不罕见,当我们有一个带有很多参数的 function 时,创建一些使用带有预定义参数的通用 function 的伴随函数。 This is the usual alternative to defining default value for parameters, something Go doesn't allow.这是定义参数默认值的通常替代方法,Go 不允许这样做。
A very simple example would be to create a SquareArea
function that uses a more general RectangleArea
function.一个非常简单的示例是创建一个SquareArea
function,它使用更通用的RectangleArea
function。
func SquareArea(side int) int {
return RectangleArea(side, side)
}
func RectangleArea(width int, length int) int {
if width < 0 || length < 0 {
return 0
}
return width * length
}
For theses functions one can create the (kinda) table tests对于这些功能,可以创建(有点)表测试
func TestSquareArea(t *testing.T) {
tests := []struct {
side int
expected int
}{
{side: -1, expected: 0},
{side: 0, expected: 0},
{side: 1, expected: 1},
{side: 2, expected: 4},
{side: 100, expected: 10000},
}
for idx, test := range tests {
if result := SquareArea(test.side); test.expected != result {
t.Errorf("test #%d expected != result (%d!=%d)", idx, test.expected, result)
}
}
}
func TestRectangleArea(t *testing.T) {
tests := []struct {
width int
length int
expected int
}{
{width: -1, length: -1, expected: 0},
{width: -1, length: 1, expected: 0},
{width: 1, length: -1, expected: 0},
{width: 2, length: 1, expected: 2},
{width: 1, length: 2, expected: 2},
{width: 3, length: 2, expected: 6},
{width: 2, length: 3, expected: 6},
{width: 10, length: 100, expected: 1000},
{width: 100, length: 10, expected: 1000},
}
for idx, test := range tests {
if result := RectangleArea(test.width, test.length); test.expected != result {
t.Errorf("test #%d expected != result (%d!=%d)", idx, test.expected, result)
}
}
}
Is this the right way?这是正确的方法吗?
For more complex functions, and more companion-functions , it seems that this approach would create a lot of tests while essentially testing several times the same underlying code (ie RectangleArea
).对于更复杂的功能和更多的配套功能,这种方法似乎会创建大量测试,同时实质上会多次测试相同的底层代码(即RectangleArea
)。
I feel that testing SquareArea
should only ensure that RectangleArea
is called with the correct parameters.我觉得测试SquareArea
应该只确保使用正确的参数调用RectangleArea
。
Is this code approach (having there both functions RectangleArea
and SquareArea
) idiomatic in Golang?这种代码方法(同时具有RectangleArea
和SquareArea
功能)在 Golang 中是惯用的吗? Is there another way to do it on Golang?在 Golang 上还有其他方法吗?
With a language like Python, I would mock the RectangleArea
function.使用像 Python 这样的语言,我会模拟RectangleArea
function。
To mimic this, I would do something like:为了模仿这一点,我会做类似的事情:
var rectangleAreaFunction = RectangleArea
func SquareArea(side int) int {
return rectangleAreaFunction(side, side)
}
func RectangleArea(width int, length int) int {
if width < 0 || length < 0 {
return 0
}
return width * length
}
with the tests:通过测试:
func TestSquareArea(t *testing.T) {
call:= ""
mock:= func(width int, length int) int {
call = fmt.Sprintf("func(%d,%d)", width, length)
return 0
}
rectangleAreaFunction = mock
tests := []struct {
side int
expected string
}{
{side: -1, expected: "func(-1,-1)"},
{side: 0, expected: "func(0,0)"},
{side: 1, expected: "func(1,1)"},
{side: 2, expected: "func(2,2)"},
{side: 100, expected: "func(100,100)"},
}
for idx, test := range tests {
call = ""
SquareArea(test.side)
if test.expected != call {
t.Errorf("test #%d expected != call (%s!=%s)", idx, test.expected, call)
}
}
}
func TestRectangleArea(t *testing.T) {
tests := []struct {
width int
length int
expected int
}{
{width: -1, length: -1, expected: 0},
{width: -1, length: 1, expected: 0},
{width: 1, length: -1, expected: 0},
{width: 2, length: 1, expected: 2},
{width: 1, length: 2, expected: 2},
{width: 3, length: 2, expected: 6},
{width: 2, length: 3, expected: 6},
{width: 10, length: 100, expected: 1000},
{width: 100, length: 10, expected: 1000},
}
for idx, test := range tests {
if result := RectangleArea(test.width, test.length); test.expected != result {
t.Errorf("test #%d expected != result (%d!=%d)", idx, test.expected, result)
}
}
}
But it seems that using a variable to hold the actual function in order to do the tests is bad.但是似乎使用变量来保存实际的 function 来进行测试是不好的。
Another example of this kind of situation would be when treating the http request method through a first triage function:这种情况的另一个例子是通过第一次分类 function 处理 http 请求方法时:
func Triage(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
// some other common code like adding some context
*r = *(r.WithContext(context.WithValue(r.Context(), "key", "value")))
switch r.Method {
case POST:
triagePost(w, r)
default:
triageGet(w, r)
}
}
func triagePost(w http.ResponseWriter, r *http.Request) {
// some code
}
func triageGet(w http.ResponseWriter, r *http.Request) {
// some code
}
Full fledged tests for the 3 functions would have a lot of useless repetitions Limiting the test of the Triage
function to:对这 3 个功能的完整测试将有很多无用的重复将Triage
function 的测试限制为:
triagePost
or triageGet
) is correctly called based on the r.Method
检查是否根据r.Method
正确调用了正确的 function( triagePost
或triageGet
)would prevent such repetitions while testing the actual behavior.将在测试实际行为时防止此类重复。
Or having this type of code (triage function) is totally incorrect in Golang?或者在 Golang 中拥有这种类型的代码(分类功能)是完全不正确的?
Go refrains from mocking as much as possible. Go尽量避免mocking。 Let's try to follow the idiomatic approach (test the real code) and see how far it brings us.让我们尝试遵循惯用的方法(测试真实代码),看看它能带给我们多远。
Function SquareArea calls RectangleArea with the same value for the two parameters. Function SquareArea 使用相同的两个参数值调用 RectangleArea。 Testing SquareArea is not concerned about the correctness of RectangleArea.测试 SquareArea 并不关心 RectangleArea 的正确性。
A single test case for SquareArea will give 100% test coverage of it, there is not even the need for a table driven test. SquareArea 的单个测试用例将提供 100% 的测试覆盖率,甚至不需要表驱动测试。
If there is concern to test the correctness of RectangleArea when given the two parameters with the same value, then this test case should be added directly to the table-driven test for RectangleArea.如果担心在给定两个相同值的参数时测试 RectangleArea 的正确性,则应将此测试用例直接添加到 RectangleArea 的表驱动测试中。
For sure, if SquareArea were more complicated, then we would need a table driven test.当然,如果 SquareArea 更复杂,那么我们将需要一个表驱动测试。 But also in this case, we would have to focus on what SquareArea does, not on what RectangleArea does.但在这种情况下,我们也必须关注 SquareArea 的作用,而不是RectangleArea 的作用。
Said in another way, when deciding how to test a function (or anything actually), I have found that the following approach helps reducing doubts:换句话说,在决定如何测试 function(或任何实际的东西)时,我发现以下方法有助于减少疑虑:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.