如何编写 sort.Slice() 的测试?

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

How do you write a test for sort.Slice()?

问题

给定sort.Slice(array, func(int, int) bool)如何在传入的函数中使用数组,你会如何为匿名函数编写测试?

来自go文档中sort.Slice()的示例代码如下:

package main

import (
	"fmt"
	"sort"
)

func main() {
	people := []struct {
		Name string
		Age  int
	}{
		{"Gopher", 7},
		{"Alice", 55},
		{"Vera", 24},
		{"Bob", 75},
	}
	sort.Slice(people, func(i, j int) bool { return people[i].Name < people[j].Name })
	fmt.Println("By name:", people)

	sort.Slice(people, func(i, j int) bool { return people[i].Age < people[j].Age })
	fmt.Println("By age:", people)
}

如果将匿名函数拆分为两个单独的函数(例如comparePeopleByName(i, j int)comparePeopleByAge(i, j, int)),你会如何测试它们?

将函数与闭包绑定在一起似乎是一个奇怪的想法。

注意:我认为测试sort.Slice()本身是不好的做法;它是语言自带的,不应该(需要?)进行测试。

英文:

Given the how sort.Slice(array, func(int, int) bool) uses the array in the passed in function, how would you write a test for the anonymous function?

The example code from the go documentation for sort.Slice() is:

package main

import (
	&quot;fmt&quot;
	&quot;sort&quot;
)

func main() {
	people := []struct {
		Name string
		Age  int
	}{
		{&quot;Gopher&quot;, 7},
		{&quot;Alice&quot;, 55},
		{&quot;Vera&quot;, 24},
		{&quot;Bob&quot;, 75},
	}
	sort.Slice(people, func(i, j int) bool { return people[i].Name &lt; people[j].Name })
	fmt.Println(&quot;By name:&quot;, people)

	sort.Slice(people, func(i, j int) bool { return people[i].Age &lt; people[j].Age })
	fmt.Println(&quot;By age:&quot;, people)
}

If you broke out the anonymous functions into two separate functions (e.g., comparePeopleByName(i, j int) and comparePeopleByAge(i, j, int)) how would you test them?

It seems like such a weird idea to tie the function to the closure.

Note: I'd consider it bad form to test sort.Slice() itself; it ships with the language and shouldn't (need to?) be tested.

答案1

得分: 4

将要测试的功能移动到一个命名函数中。测试该函数。

type Person struct {
    Name string
    Age  int
}

func personLessByName(people []Person, i, j int) bool { 
    return people[i].Name < people[j].Name 
}

func personLessByAge(people []Person, i, j int) bool {
    return people[i].Age < people[j].Age
}

在调用sort.Slice时使用该函数:

sort.Slice(people, func(i, j int) bool { return personLessByName(people, i, j) })

sort.Slice(people, func(i, j int) bool { return personLessByAge(people, i, j) })

以下是一个测试示例:

func TestComparePeople(t *testing.T) {
    cases := []struct {
        desc     string
        a, b     Person
        fn       func([]Person, int, int) bool
        expected bool
    }{
        {"name a < b", Person{"bill", 10}, Person{"melinda", 20}, personLessByName, true},
        {"name a > b", Person{"melinda", 30}, Person{"bill", 20}, personLessByName, false},
        {"name a = b", Person{"melinda", 90}, Person{"melinda", 90}, personLessByName, false},
        {"age a < b", Person{"melinda", 10}, Person{"bill", 20}, personLessByAge, true},
        {"age a > b", Person{"melinda", 30}, Person{"bill", 20}, personLessByAge, false},
        {"age a = b", Person{"melinda", 90}, Person{"bill", 90}, personLessByAge, false},
    }
    for _, c := range cases {
        people := []Person{c.a, c.b}
        got := c.fn(people, 0, 1)
        if c.expected != got {
            t.Errorf("%s: expected %v, got %v", c.desc, c.expected, got)
        }
    }
}
英文:

Move the functionality that you want to test to a named function. Test that function.

type Person struct {
    Name string
    Age  int
}

func personLessByName(people []Person, i, j int) bool { 
        return people[i].Name &lt; people[j].Name 
}

func personLessByAge(people []Person, i, j int) bool {
	return people[i].Age &lt; people[j].Age
}

Use the function in the call to sort.Slice:

sort.Slice(people, func(i, j int) bool { return personLessByName(people, i, j) })

sort.Slice(people, func(i, j int) bool { return personLessByAge(people, i, j) })

Here'a test:

func TestComparePeople(t *testing.T) {
	cases := []struct {
		desc     string
		a, b     Person
		fn       func([]Person, int, int) bool
		expected bool
	}{
		{&quot;name a &lt; b&quot;, Person{&quot;bill&quot;, 10}, Person{&quot;melinda&quot;, 20}, personLessByName, true},
		{&quot;name a &gt; b&quot;, Person{&quot;melinda&quot;, 30}, Person{&quot;bill&quot;, 20}, personLessByName, false},
		{&quot;name a = b&quot;, Person{&quot;melinda&quot;, 90}, Person{&quot;melinda&quot;, 90}, personLessByName, false},
		{&quot;age a &lt; b&quot;, Person{&quot;melinda&quot;, 10}, Person{&quot;bill&quot;, 20}, personLessByAge, true},
		{&quot;age a &gt; b&quot;, Person{&quot;melinda&quot;, 30}, Person{&quot;bill&quot;, 20}, personLessByAge, false},
		{&quot;age a = b&quot;, Person{&quot;melinda&quot;, 90}, Person{&quot;bill&quot;, 90}, personLessByAge, false},
	}
	for _, c := range cases {
		people := []Person{c.a, c.b}
		got := c.fn(people, 0, 1)
		if c.expected != got {
			t.Errorf(&quot;%s: expected %v, got %v&quot;, c.desc, c.expected, got)
		}
	}
}

答案2

得分: -1

在稍微尝试了一下之后,我写了这段代码。它使用一个工厂函数来创建方法,以便将切片包含在函数的闭包中。

我将它放在了一个 gist 中,以便轻松地进行测试:https://gist.github.com/docwhat/e3b13265d24471651e02f7d7a42e7d2c

// main.go
package main

import (
	"fmt"
	"sort"
)

type Person struct {
	Name string
	Age  int
}

func comparePeopleByName(people []Person) func(int, int) bool {
	return func(i, j int) bool {
		return people[i].Name < people[j].Name
	}
}

func comparePeopleByAge(people []Person) func(int, int) bool {
	return func(i, j int) bool {
		return people[i].Age < people[j].Age
	}
}

func main() {
	people := []Person{
		{"Gopher", 7},
		{"Alice", 55},
		{"Vera", 24},
		{"Bob", 75},
	}
	sort.Slice(people, comparePeopleByName(people))
	fmt.Println("按姓名排序:", people)

	sort.Slice(people, comparePeopleByAge(people))
	fmt.Println("按年龄排序:", people)
}
// main_test.go
package main

import "testing"

func TestComparePeopleByName(t *testing.T) {
	testCases := []struct {
		desc     string
		a, b     Person
		expected bool
	}{
		{"a < b", Person{"bob", 1}, Person{"krabs", 2}, true},
		{"a > b", Person{"krabs", 1}, Person{"bob", 2}, false},
		{"a = a", Person{"plankton", 1}, Person{"plankton", 2}, false},
	}

	for _, testCase := range testCases {
		t.Run(testCase.desc, func(t *testing.T) {
			people := []Person{testCase.a, testCase.b}
			got := comparePeopleByName(people)(0, 1)
			if testCase.expected != got {
				t.Errorf("期望 %v,得到 %v", testCase.expected, got)
			}
		})
	}
}

func TestComparePeopleByAge(t *testing.T) {
	testCases := []struct {
		desc     string
		a, b     Person
		expected bool
	}{
		{"a < b", Person{"sandy", 10}, Person{"patrick", 20}, true},
		{"a > b", Person{"sandy", 30}, Person{"patrick", 20}, false},
		{"a = b", Person{"sandy", 90}, Person{"patrick", 90}, false},
	}

	for _, testCase := range testCases {
		t.Run(testCase.desc, func(t *testing.T) {
			people := []Person{testCase.a, testCase.b}
			got := comparePeopleByAge(people)(0, 1)
			if testCase.expected != got {
				t.Errorf("期望 %v,得到 %v", testCase.expected, got)
			}
		})
	}
}
英文:

After messing around a little, I wrote this bit of code. It uses a factory to create the method so that the slice is contained in the closure for the function.

I stuck it in a gist for easy playing around with: https://gist.github.com/docwhat/e3b13265d24471651e02f7d7a42e7d2c

// main.go
package main

import (
	&quot;fmt&quot;
	&quot;sort&quot;
)

type Person struct {
	Name string
	Age  int
}

func comparePeopleByName(people []Person) func(int, int) bool {
	return func(i, j int) bool {
		return people[i].Name &lt; people[j].Name
	}
}

func comparePeopleByAge(people []Person) func(int, int) bool {
	return func(i, j int) bool {
		return people[i].Age &lt; people[j].Age
	}
}

func main() {
	people := []Person{
		{&quot;Gopher&quot;, 7},
		{&quot;Alice&quot;, 55},
		{&quot;Vera&quot;, 24},
		{&quot;Bob&quot;, 75},
	}
	sort.Slice(people, comparePeopleByName(people))
	fmt.Println(&quot;By name:&quot;, people)

	sort.Slice(people, comparePeopleByAge(people))
	fmt.Println(&quot;By age:&quot;, people)
}
// main_test.go
package main

import &quot;testing&quot;

func TestComparePeopleByName(t *testing.T) {
	testCases := []struct {
		desc     string
		a, b     Person
		expected bool
	}{
		{&quot;a &lt; b&quot;, Person{&quot;bob&quot;, 1}, Person{&quot;krabs&quot;, 2}, true},
		{&quot;a &gt; b&quot;, Person{&quot;krabs&quot;, 1}, Person{&quot;bob&quot;, 2}, false},
		{&quot;a = a&quot;, Person{&quot;plankton&quot;, 1}, Person{&quot;plankton&quot;, 2}, false},
	}

	for _, testCase := range testCases {
		t.Run(testCase.desc, func(t *testing.T) {
			people := []Person{testCase.a, testCase.b}
			got := comparePeopleByName(people)(0, 1)
			if testCase.expected != got {
				t.Errorf(&quot;expected %v, got %v&quot;, testCase.expected, got)
			}
		})
	}
}

func TestComparePeopleByAge(t *testing.T) {
	testCases := []struct {
		desc     string
		a, b     Person
		expected bool
	}{
		{&quot;a &lt; b&quot;, Person{&quot;sandy&quot;, 10}, Person{&quot;patrick&quot;, 20}, true},
		{&quot;a &gt; b&quot;, Person{&quot;sandy&quot;, 30}, Person{&quot;patrick&quot;, 20}, false},
		{&quot;a = b&quot;, Person{&quot;sandy&quot;, 90}, Person{&quot;patrick&quot;, 90}, false},
	}

	for _, testCase := range testCases {
		t.Run(testCase.desc, func(t *testing.T) {
			people := []Person{testCase.a, testCase.b}
			got := comparePeopleByAge(people)(0, 1)
			if testCase.expected != got {
				t.Errorf(&quot;expected %v, got %v&quot;, testCase.expected, got)
			}
		})
	}
}

huangapple
  • 本文由 发表于 2022年7月21日 02:15:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/73056418.html
匿名

发表评论

匿名网友

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

确定