Saya memiliki kasus uji berbasis tabel seperti ini:
func CountWords(s string) map[string]int
func TestCountWords(t *testing.T) {
var tests = []struct {
input string
want map[string]int
}{
{"foo", map[string]int{"foo":1}},
{"foo bar foo", map[string]int{"foo":2,"bar":1}},
}
for i, c := range tests {
got := CountWords(c.input)
// TODO test whether c.want == got
}
}
Saya dapat memeriksa apakah panjangnya sama dan menulis loop yang memeriksa apakah setiap pasangan nilai kunci sama. Tapi kemudian saya harus menulis centang ini lagi ketika saya ingin menggunakannya untuk jenis peta lain (katakanlah map[string]string
).
Apa yang akhirnya saya lakukan adalah, saya mengonversi peta menjadi string dan membandingkan string:
func checkAsStrings(a,b interface{}) bool {
return fmt.Sprintf("%v", a) != fmt.Sprintf("%v", b)
}
//...
if checkAsStrings(got, c.want) {
t.Errorf("Case #%v: Wanted: %v, got: %v", i, c.want, got)
}
Ini mengasumsikan bahwa representasi string dari peta yang setara adalah sama, yang tampaknya benar dalam kasus ini (jika kuncinya sama maka mereka memiliki nilai yang sama, sehingga urutannya akan sama). Apakah ada cara yang lebih baik untuk melakukan ini? Apa cara idiomatik untuk membandingkan dua peta dalam pengujian berbasis tabel?
testing
maps
go
equivalence
table-driven
andras
sumber
sumber
Jawaban:
Perpustakaan Go telah membantu Anda. Melakukan hal ini:
import "reflect" // m1 and m2 are the maps we want to compare eq := reflect.DeepEqual(m1, m2) if eq { fmt.Println("They're equal.") } else { fmt.Println("They're unequal.") }
Jika Anda melihat kode sumber untuk
reflect.DeepEqual
'sMap
kasus, Anda akan melihat bahwa itu cek pertama jika kedua peta yang nihil, maka cek jika mereka memiliki panjang yang sama sebelum akhirnya memeriksa untuk melihat apakah mereka memiliki set yang sama (kunci, nilai) pasangan.Karena
reflect.DeepEqual
menggunakan tipe antarmuka, ini akan bekerja pada peta yang valid (map[string]bool, map[struct{}]interface{}
, dll). Perhatikan bahwa ini juga akan berfungsi pada nilai non-peta, jadi berhati-hatilah karena apa yang Anda teruskan ke sana sebenarnya adalah dua peta. Jika Anda memberikan dua bilangan bulat, itu akan dengan senang hati memberi tahu Anda apakah keduanya sama.sumber
c.Assert(m1, DeepEquals, m2)
. Apa yang menyenangkan tentang ini adalah membatalkan pengujian dan memberi tahu Anda apa yang Anda dapatkan dan apa yang Anda harapkan dalam output.Anda memiliki proyek
go-test/deep
untuk membantu.Tetapi: ini seharusnya lebih mudah dengan Go 1.12 (Februari 2019) secara native : Lihat catatan rilis .
Sumber:
golang/go
Masalah 21095 ,purpleidea
)CL menambahkan: ( CL adalah singkatan dari "Change List" )
Gunakan juga paket di
text/template
, yang sudah memiliki versi yang lebih lemah dari mekanisme ini.Anda dapat melihat bahwa digunakan dalam
src/fmt/print.go#printValue(): case reflect.Map:
sumber
fmt
perilaku baru ini membantu menguji kesetaraan peta? Apakah Anda menyarankan untuk membandingkan representasi string daripada menggunakanDeepEqual
?DeepEqual
masih bagus. (atau lebih tepatnyacmp.Equal
) Kasus penggunaan lebih diilustrasikan di twitter.com/mikesample/status/1084223662167711744 , seperti log yang berbeda seperti yang dinyatakan dalam masalah asli: github.com/golang/go/issues/21095 . Artinya: bergantung pada sifat pengujian Anda, perbedaan yang andal dapat membantu.fmt.Sprint(map1) == fmt.Sprint(map2)
untuk tl; drInilah yang akan saya lakukan (kode belum teruji):
func eq(a, b map[string]int) bool { if len(a) != len(b) { return false } for k, v := range a { if w, ok := b[k]; !ok || v != w { return false } } return true }
sumber
map[string]float64
.eq
hanya berfungsi untukmap[string]int
peta. Haruskah saya menerapkan versieq
fungsi setiap kali saya ingin membandingkan instance dari tipe peta baru?a
.Penafian : Tidak
map[string]int
terkait tetapi terkait dengan pengujian kesetaraan peta di Go, yang merupakan judul pertanyaanJika Anda memiliki peta tipe pointer (seperti
map[*string]int
), maka Anda jangan tidak ingin menggunakan reflect.DeepEqual karena akan kembali palsu.Terakhir, jika kuncinya adalah tipe yang berisi penunjuk yang tidak diekspor, seperti time.Time, maka reflect.DeepEqual pada peta seperti itu juga dapat mengembalikan false .
sumber
Gunakan metode "Diff" dari github.com/google/go-cmp/cmp :
Kode:
// Let got be the hypothetical value obtained from some logic under test // and want be the expected golden data. got, want := MakeGatewayInfo() if diff := cmp.Diff(want, got); diff != "" { t.Errorf("MakeGatewayInfo() mismatch (-want +got):\n%s", diff) }
Keluaran:
MakeGatewayInfo() mismatch (-want +got): cmp_test.Gateway{ SSID: "CoffeeShopWiFi", - IPAddress: s"192.168.0.2", + IPAddress: s"192.168.0.1", NetMask: net.IPMask{0xff, 0xff, 0x00, 0x00}, Clients: []cmp_test.Client{ ... // 2 identical elements {Hostname: "macchiato", IPAddress: s"192.168.0.153", LastSeen: s"2009-11-10 23:39:43 +0000 UTC"}, {Hostname: "espresso", IPAddress: s"192.168.0.121"}, { Hostname: "latte", - IPAddress: s"192.168.0.221", + IPAddress: s"192.168.0.219", LastSeen: s"2009-11-10 23:00:23 +0000 UTC", }, + { + Hostname: "americano", + IPAddress: s"192.168.0.188", + LastSeen: s"2009-11-10 23:03:05 +0000 UTC", + }, }, }
sumber
Gunakan cmp ( https://github.com/google/go-cmp ) sebagai gantinya:
if !cmp.Equal(src, expectedSearchSource) { t.Errorf("Wrong object received, got=%s", cmp.Diff(expectedSearchSource, src)) }
Itu masih gagal ketika peta "urutan" dalam output yang Anda harapkan tidak sesuai dengan fungsi yang dikembalikan. Namun,
cmp
masih bisa menunjukkan di mana letak ketidakkonsistenannya.Sebagai referensi, saya telah menemukan tweet ini:
https://twitter.com/francesc/status/885630175668346880?lang=en
sumber
Cara termudah:
assert.InDeltaMapValues(t, got, want, 0.0, "Word count wrong. Got %v, want %v", got, want)
Contoh:
import ( "github.com/stretchr/testify/assert" "testing" ) func TestCountWords(t *testing.T) { got := CountWords("hola hola que tal") want := map[string]int{ "hola": 2, "que": 1, "tal": 1, } assert.InDeltaMapValues(t, got, want, 0.0, "Word count wrong. Got %v, want %v", got, want) }
sumber
Salah satu opsinya adalah dengan memperbaiki rng:
rand.Reader = mathRand.New(mathRand.NewSource(0xDEADBEEF))
sumber