Cara menguji pipeline saluran di Go

Aug 15 2020

Saya cukup banyak menggunakan pola "saluran pipa" di Go, yang terlihat seperti ini:

// getSomeNums spits out ints onto a channel. Temperatures, pressures, doesn't matter
func getSomeNums(ch chan<- int) {
    // Imagination goes here
}

// double takes numbers from in, doubles them, and pushes them into out
func double(in <-chan int, out chan<- int) {
    for v := range in {
        out <- v * 2
    close(out)
}

source := make(chan int)
go getSomeNums(source)

doubles := make(chan int)
double(source, doubles)

Masalah yang berulang kali saya hadapi, adalah saya harus menguji sejumlah fitur berbeda dari fungsi pipeline ini:

  • Menempatkan nilai pada saluran keluaran saat Anda meletakkannya di saluran masukan
  • Tidak memberi nilai pada saluran keluaran saat Anda tidak meletakkannya di saluran masukan
  • Waktu habis jika saluran keluaran terlalu lama setelah nilai muncul di saluran masukan
  • Menutup saluran keluaran saat saluran masukan ditutup
  • Tidak menutup saluran keluaran sebelum saluran masukan ditutup
  • Melakukan transformasi yang benar pada input

Terlebih lagi, ini hanya contoh yang sangat sederhana. Kasus yang lebih umum terlihat seperti contoh ini, di mana kami mencoba menggunakan sensor suhu yang berlebihan untuk menemukan kesalahan dalam output:

// Provided we have channels for sensorA, sensorB, and sensorC
import "math"

LIMIT = 0.1   // Set acceptable variation limit between sensors to 10%

type SafeTemp struct {
    Temp float64
    isSafe bool
}

// variation returns relative error between inputs. Unfortunately, "error" was taken
func variation(a, b float64) float64 {
    return math.Abs((a - b) / (a + b))
}

// safify zips together temperatures so long as error is below LIMIT
func safify(chA, chB, chC <-chan float64, chOut chan<- SafeTemp) {
    for {
        a, aOk := <-chA
        b, bOk := <-chB
        c, cOk := <-chC

        if !(aOk && bOk && cOk) {
            close(chOut)
            return
        }

        if variation(a, b) < LIMIT && variation(b, c) < LIMIT &&
                variation(c, a) < LIMIT {
            chOut <- SafeTemp{ (a + b + c) / 3, true }
        } else {
            chOut <- SafeTemp{ 0.0, false }
        }

    }
}

Sekarang jumlah hal yang harus saya uji untuk fungsi pipeline ( safify) meningkat secara signifikan:

  • Memberi nilai pada saluran keluaran saat Anda mendapatkannya di semua saluran masukan
  • Tidak memberi nilai pada saluran keluaran jika Anda tidak mendapatkannya di semua saluran masukan
  • Waktu habis jika saluran keluaran terlalu lama setelah masukan pada ketiga saluran masukan, tetapi hanya ketiganya
  • Menutup saluran keluaran ketika saluran masukan apa pun ditutup
  • Tidak menutup saluran keluaran jika tidak ada saluran masukan yang ditutup
  • Panjikan seolah- isSafeolah saluran pertama berbeda secara signifikan dari yang lain, dengan waktu tunggu
  • Panjikan seolah- isSafeolah saluran kedua berbeda secara signifikan dari yang lain, dengan batas waktu
  • Panjikan seolah- isSafeolah saluran ketiga berbeda secara signifikan dari yang lain, dengan batas waktu
  • Panjikan seolah- isSafeolah semua saluran berbeda secara signifikan dari yang lain, dengan batas waktu

Lebih lanjut, tiga saluran input bisa tidak sinkron satu sama lain, yang menambah kompleksitas signifikan yang masih melebihi yang ditunjukkan di atas.

Tampaknya banyak pemeriksaan ini (kecuali secara khusus yang harus dilakukan dengan perhitungan yang benar) pada dasarnya umum untuk semua fungsi saluran saluran bergaya penggemar di Go, dan Masalah Halting menjamin bahwa kita harus menggunakan batas waktu untuk semua. operasi ini, kecuali kita ingin penghentian pengujian kita bergantung pada perilaku penghentian dan akhirnya mendorong saluran dari fungsi yang diuji.

Mengingat betapa miripnya jenis pengujian ini di seluruh papan, dan bagaimana saya akhirnya menulis pengujian yang sangat mirip pada dasarnya menguji seberapa baik fungsi saluran pipa ini sesuai dengan jaminan fungsi saluran saluran dasar , alih-alih perilaku fungsi , berulang kali , apakah ada:

  1. Serangkaian standar praktik seputar jenis uji keandalan fungsi saluran pipa ini ATAU
  2. Kerangka kerja standar atau yang diperkuat dengan baik atau serangkaian kerangka kerja untuk menguji fungsi saluran-asli?

Jawaban

2 KarlBielefeldt Aug 16 2020 at 18:27

Anda mencampurkan dua masalah berbeda. Jika Anda membuat abstraksi terpisah untuk pipeline, Anda bisa mengujinya sekali. Sesuatu seperti (maafkan sintaks, saya tidak tahu pergi):

func double(v int) int {
    return v * 2
}

pipeline(in, out, double)

atau

func safe(v [3]float64) SafeTemp {
    if variation(v[0], v[1]) < LIMIT && variation(v[1], v[2]) < LIMIT &&
            variation(v[2], v[0]) < LIMIT {
        return SafeTemp{ (v[0] + v[1] + v[2]) / 3, true }
    } else {
        return SafeTemp{ 0.0, false }
    }
}

pipeline(in, out, safe)

Tanpa polimorfisme parametrik, Anda tidak dapat benar-benar membuat pipelineabstraksi yang sepenuhnya umum , jadi Anda harus menerima sejumlah duplikasi. Namun, Anda setidaknya harus dapat memisahkan masalah pola pipeline dari logika yang lebih spesifik aplikasi.