Soheil Hassas La soluzione di Yeganeh è di solito una buona strada da percorrere, o almeno qualcosa di simile. Ma è una modifica all'API, e può creare un sovraccarico per il chiamante (anche se non molto, il chiamante non ha ha per passare un canale Done
se il chiamante non ne ha bisogno). Detto questo, ci sono casi in cui non si desidera quel tipo di sistema ACK.
Consiglio vivamente il pacchetto di prova Gomega per questo tipo di problema. È progettato per funzionare con Ginkgo, ma può essere utilizzato autonomamente. Include un eccellente supporto asincrono tramite gli abbinamenti Consistently
e Eventually
.
Detto questo, mentre Gomega funziona bene con i sistemi di test non BDD (e si integra perfettamente in testing
), è una cosa piuttosto grande e può essere un impegno. Se vuoi solo quell'unico pezzo, puoi scrivere la tua versione di queste asserzioni. Ti consiglio comunque di seguire l'approccio di Gomega, che esegue il polling piuttosto che un singolo sleep (questo dorme ancora, non è possibile risolverlo senza ridisegnare l'API).
Ecco come osservare le cose in prova. Si crea una funzione di supporto del tipo:
http://play.golang.org/p/qpdEOsWYh0
const iterations = 10
const interval = time.Millisecond
func Consistently(f func()) {
for i := 0; i < iterations; i++ {
f() // Assuming here that `f()` panics on failure
time.Sleep(interval)
}
}
mock.devices <- []sparkapi.Device{deviceA, deviceFuncs, deviceRefresh}
Consistently(c.Check(mock.actionArgs, check.DeepEquals, mockFunctionCall{}))
Ovviamente è possibile modificare le iterazioni e l'intervallo per soddisfare le vostre esigenze. (Gomega usa un timeout di 1 secondo, polling ogni 10ms.)
Lo svantaggio di qualsiasi implementazione di Consistently
è che qualunque sia il tuo timeout, devi mangiare ogni test eseguito. Ma non c'è davvero alcun modo per aggirare questo. Devi decidere quanto è lungo abbastanza per "non accadere". Quando possibile, è bello girare il test in giro per verificare Eventually
, dal momento che può avere successo più velocemente.
Eventually
è un po 'più complicato, dal momento che è necessario utilizzare recover
per prendere il panico fino a quando non riesce, ma non è male.Qualcosa di simile a questo:
func Eventually(f func()) {
for i := 0; i < iterations; i++ {
if !panics(f) {
return
}
time.Sleep(interval)
}
panic("FAILED")
}
func panics(f func()) (success bool) {
defer func() {
if e := recover(); e != nil {
success = true
}
}()
f()
return
}
In definitiva, questa è solo una versione leggermente più complicata di quello che hai, ma avvolge la logica su in una funzione in modo che legge un po 'meglio.
Stavo cercando di mantenere la domanda semplice, ma forse ho finito di farlo .. Sto inviando dati a un componente che può passare ad altri componenti, che possono passare ad altri componenti. La catena di passaggio può includere rami e forche e tutti i tipi di complessità. Questo rende questo (altrimenti eccellente) suggerimento non così utile. – DonGar
Sì, questo è il punto. Tutti i componenti della catena dovrebbero ricevere il canale 'done' come parte del loro lavoro aysnc. Altrimenti, sprecherai CPU e le tue go-routine dovranno svegliarsi e sondare. –
@DonGar Potresti anche essere interessato a 'net/context' e http://blog.golang.org/pipelines. Esistono alcuni modelli Go esistenti molto validi per catene complesse di componenti incrociati. (Non cambia la domanda di testabilità, ma può influenzare la progettazione della concorrenza.) –