Ci scusiamo per il necro in questo post, ma mi sento in dovere di ponderare un paio di cose che non sembrano essere state toccate.
Innanzitutto - quando ci troviamo a dover accedere a membri privati in una classe durante i test unitari, è generalmente una grande e grossa bandiera rossa che abbiamo imbrogliato nel nostro approccio strategico o tattico e abbiamo inavvertitamente violato il singolo responsabile della responsabilità spingendo comportamento a cui non appartiene. Sentire la necessità di accedere a metodi che non sono altro che una subroutine isolata di una procedura di costruzione è uno degli eventi più comuni di questo; tuttavia, è un po 'come se il tuo capo si aspettasse che ti presentassi per un lavoro pronto e che avesse anche qualche perverso bisogno di sapere quale routine mattutina hai passato per portarti in quello stato ...
L'altro caso più comune di questo evento è quando ti ritrovi a provare la proverbiale "classe del dio". Si tratta di un tipo speciale di problema in sé e per sé, ma soffre dello stesso problema di base con la necessità di conoscere i dettagli intimi di una procedura, ma questo è fuori tema.
In questo esempio specifico, abbiamo effettivamente assegnato la responsabilità di inizializzare completamente l'oggetto Bar al costruttore della classe FooBar. Nella programmazione orientata agli oggetti, uno dei principali inquilini è che il costruttore è "sacro" e dovrebbe essere protetto da dati non validi che invaliderebbero il proprio stato interno e lo lascerebbero innescato per fallire altrove a valle (in quello che potrebbe essere un profondo tubatura.)
Non siamo riusciti a farlo qui consentendo all'oggetto FooBar di accettare una barra che non è pronta al momento della costruzione del FooBar e che abbiamo compensato con una sorta di "hacking" dell'oggetto FooBar per prendere le cose in proprio mani.
Questo è il risultato di una mancata adesione a un altro tenente di programmazione orientata agli oggetti (nel caso di Bar,) che è che lo stato di un oggetto dovrebbe essere completamente inizializzato e pronto a gestire qualsiasi chiamata in entrata verso i suoi "membri pubblici immediatamente dopo la creazione. Ora, questo non significa immediatamente dopo che il costruttore è stato chiamato in tutte le istanze. Quando si dispone di un oggetto con molti scenari di costruzione complessi, è meglio esporre i setter ai suoi membri opzionali a un oggetto che viene implementato secondo un modello di progettazione della creazione (Factory, Builder, ecc ...) questi ultimi casi,
Nel tuo esempio, la proprietà "status" della barra non sembra essere in uno stato valido in cui un FooBar può accettarlo, quindi FooBar fa qualcosa per correggerlo.
Il secondo problema che sto vedendo è che sembra che tu stia provando a testare il tuo codice piuttosto che praticare lo sviluppo guidato dai test. Questa è sicuramente la mia opinione in questo momento; ma, questo tipo di test è davvero un anti-pattern. Quello che finisci per fare è cadere nella trappola della consapevolezza di avere problemi di progettazione fondamentali che impediscono al codice di essere testabile dopo il fatto, piuttosto che scrivere i test necessari e successivamente programmarli. In entrambi i casi, dovresti comunque ottenere lo stesso numero di test e righe di codice se avessi davvero raggiunto un'implementazione SOLID. Quindi - perché provare a decodificare la tua strada nel codice testabile quando puoi semplicemente affrontare la questione all'inizio dei tuoi sforzi di sviluppo?
Se lo avessi fatto, allora ti saresti reso conto molto prima che avresti dovuto scrivere un po 'di codice piuttosto complicato per testare il tuo design e avresti avuto presto l'opportunità di riallineare il tuo approccio spostando il comportamento su implementazioni che sono facilmente testabili.