Questa domanda mi ha infastidito per alcuni giorni e sembra che diverse pratiche si contraddicono a vicenda.
Esempio
Iterazione 1
public class FooDao : IFooDao
{
private IFooConnection fooConnection;
private IBarConnection barConnection;
public FooDao(IFooConnection fooConnection, IBarConnection barConnection)
{
this.fooConnection = fooConnection;
this.barConnection = barConnection;
}
public Foo GetFoo(int id)
{
Foo foo = fooConection.get(id);
Bar bar = barConnection.get(foo);
foo.bar = bar;
return foo;
}
}
Ora, quando eseguo il test, emetto IFooConnection e IBarConnection e utilizzo Dependency Injection (DI) durante l'istanza di FooDao.
Posso modificare l'implementazione, senza modificare la funzionalità.
Iterazione 2
public class FooDao : IFooDao
{
private IFooBuilder fooBuilder;
public FooDao(IFooConnection fooConnection, IBarConnection barConnection)
{
this.fooBuilder = new FooBuilder(fooConnection, barConnection);
}
public Foo GetFoo(int id)
{
return fooBuilder.Build(id);
}
}
Ora, non scriverò questo costruttore, ma immagino che faccia la stessa cosa che fece FooDao prima. Questo è solo un refactoring, quindi, ovviamente, questo non cambia la funzionalità, e così, il mio test passa ancora.
IFooBuilder è Internal, poiché esiste solo per fare del lavoro per la libreria, cioè non è una parte dell'API.
L'unico problema è che non rispondo più a Dependency Inversion. Se ho riscritto questo problema per risolvere il problema, potrebbe apparire come questo.
Iteration 3
public class FooDao : IFooDao
{
private IFooBuilder fooBuilder;
public FooDao(IFooBuilder fooBuilder)
{
this.fooBuilder = fooBuilder;
}
public Foo GetFoo(int id)
{
return fooBuilder.Build(id);
}
}
Questo dovrebbe farlo. Ho cambiato il costruttore, quindi il mio test deve cambiare per supportare questo (o viceversa), questo non è il mio problema però.
Affinché questo funzioni con DI, FooBuilder e IFooBuilder devono essere pubblici. Ciò significa che dovrei scrivere un test per FooBuilder, dal momento che è diventato improvvisamente parte dell'API della mia biblioteca. Il mio problema è che i clienti della biblioteca dovrebbero usarlo solo attraverso il mio design API, IFooDao, ei miei test funzionano come clienti. Se non seguo Dependency Inversion i miei test e le API sono più pulite.
In altre parole, tutto quello che mi interessa come client, o come test, è ottenere il Foo corretto, non come è costruito.
Soluzioni
-
Dovrei semplicemente non preoccuparmi, scrivere i test per FooBuilder anche se è solo pubblico per favore DI? - Supporta l'iterazione 3
-
Devo rendermi conto che espandere l'API è un aspetto negativo di Dependency Inversion e essere molto chiaro sul motivo per cui ho scelto di non rispettarlo qui? - Supporta l'iterazione 2
-
Pongo troppa enfasi sull'avere un'API pulita? - Supporta l'iterazione 3
EDIT: Voglio chiarire che il mio problema è non "Come testare gli internals?", piuttosto è qualcosa come "Posso mantenerlo interno, e comunque rispettare il DIP, e dovrei? ".