2013-02-26 11 views
12

Sto scrivendo un involucro sottile attorno Dizionario che è progettato per essere thread-safe. Di conseguenza, sono necessari alcuni blocchi e la maggior parte della logica è intorno a garantire che le cose siano bloccate correttamente e accessibili in modo thread-safe.È possibile simulare/falsificare in giro per far sì che un blocco mancante causi un test non funzionante?

Ora, sto provando a testare l'unità. Una cosa importante che vorrei testare è il comportamento di blocco, per assicurarsi che sia corretto. Tuttavia, non ho mai visto questo fatto ovunque, quindi non sono sicuro di come farlo. Inoltre, so che potrei usare solo un mucchio di thread per gettare cose al muro, ma con questo tipo di test, non c'è alcuna garanzia che fallirà quando è sbagliato. Dipende dal comportamento definito dal sistema operativo con la pianificazione dei thread.

Quali sono le modalità per garantire che il mio comportamento di blocco sia corretto con i test delle unità?

+5

+1 sul concetto della domanda; curioso perché non stai usando un "ConcurrentDictionary" per questo caso particolare. –

+0

@TimMedora libreria di classi portatili indirizzate a Windows Phone :( – Earlz

+0

Capito: dalla mia esperienza, bombardare una presunta classe thread-safe con migliaia di operazioni (cross-thread, sequenza variata) rivelerà rapidamente problemi, ma ovviamente non è deterministico. potrebbe aggiungere alcuni ganci di test interni per forzare lo scenario desiderato. –

risposta

1

non credo che con Dictionary stesso è possibile ottenere test affidabili - il vostro obiettivo è quello di fare 2 chiamate per eseguire in parallelo, che non sta per accadere in modo affidabile.

È possibile provare ad avere un dizionario personalizzato (ad esempio, derivare da uno normale) e aggiungere callback per tutti i metodi Get/Add. Sarai in grado di ritardare le chiamate in 2 thread, se necessario ... Avrai bisogno di una sincronizzazione separata tra 2 thread per far sì che il tuo codice di test funzioni nel modo desiderato e non si blocchi continuamente.

+0

Ho finito per farlo. Ho spiegato con esattezza come ho fatto questo sotto – Earlz

3

Il blocco è solo un dettaglio di implementazione. Dovresti prendere in mano la condizione di gara e verificare se i dati non hanno perso la sua integrità nel test.

Questo sarebbe un vantaggio se si decide di modificare l'implementazione del dizionario e utilizzare altre primitive di sincronizzazione.

Il test di blocco dimostrerà che si sta invocando un'istruzione di blocco dove ci si aspetta. Mentre testare con una condizione di gara derisa potrebbe rivelare luoghi che non ti aspettavi di sincronizzare.

+0

Fondamentalmente si basa su Dictionary breaking se si accede simultaneamente – Earlz

+0

No dovrebbe rimanere in uno stato corretto, quello sarebbe dimostrato dal tuo test. – Kimi

+2

Bene, si basa quindi sui dettagli di implementazione del dizionario. In realtà ho bisogno di testare che il dizionario non sia mai stato tentato di accedere in modo concorrente perché l'intera cosa non è fondamentalmente thread-safe per i miei scopi. – Earlz

2

Fondamentalmente ho finito per fare quello che ha detto @Alexei. Per essere più precisi, questo è quello che ho fatto:

  1. Fai un PausingDictionary che fondamentalmente solo ha una funzione di callback per ogni metodo, ma per il resto appena passa attraverso ad un normale dizionario
  2. astratta mio codice (usando DI, ecc) in modo da poter utilizzare PausingDictionary anziché un dizionario normale durante il test
  3. Sono stati aggiunti due ConcurrentDictionaries nel mio test di unità. Uno chiamato "Accessed" e uno chiamato "GoAhead". La chiave per thiis è una combinazione di "action"+Thread.GetHashCode().ToString() (in cui l'azione è diverso per ogni richiamata)
  4. Initialized tutto false e ha aggiunto alcuni metodi di estensione per rendere il lavoro con un po 'più facile
  5. installazione callback del dizionario per impostare accede per la thread to true, ma attenderebbe in callback fino a quando GoAhead era vero
  6. Avviato due thread all'interno del test dell'unità. Un thread accederà al dizionario, ma poiché GoAhead è falso per quella discussione, si siederebbe lì. Il secondo thread tenterebbe quindi di accedere al dizionario
  7. Avrei un'affermazione che Accessed per quel thread è falso, perché il mio codice dovrebbe bloccarlo.

C'è un po 'di più. Avrei anche bisogno di prendere in giro un IList, ma non penso che lo farò.Questi test unitari, sebbene preziosi, non sono sicuramente la cosa più facile del mondo da scrivere. A parte il codice di installazione e le implementazioni di interfacce finte e simili, ogni test finisce con circa 25 righe di codice non-boilerplate. Il blocco è difficile. Dimostrare che il blocco è efficace è ancora più difficile. Sorprendentemente, questo tipo di schema può consentire di testare quasi tutti gli scenari. Ma è molto prolisso e non è un buon test

Quindi, nonostante sia difficile scrivere i test, funziona perfettamente. Quando rimuovo un lucchetto fallisce costantemente e quando aggiungo di nuovo il lucchetto, passa costantemente.

Edit:

Penso che questo metodo di "interleave controllo" di fili potrebbe anche rendere possibile testare thread-sicurezza, dato che si scrive un test per ogni possibile interleave. Con un po 'di codice questo sarebbe impossibile, ma voglio solo dire che questo non è in alcun modo limitato al solo codice di blocco. Si potrebbe fare allo stesso modo di duplicare in modo coerente un errore thread-safe come foo.Contains(x) e poi var tmp=foo[x]

+0

+1: molto bella la scrittura! –

Problemi correlati