Test unitari con NSURLConnection
-
03-07-2019 - |
Domanda
Voglio testare un pezzo di codice che utilizza la rete (la classe NSURLConnection
, per essere specifici). Il codice (chiamiamolo NetworkManager
) è un po 'così:
- (id) buildConnection
{
// some more code and then:
return [NSURLConnection …];
}
- (void) startNetworkSync
{
id connection = [self buildConnection];
//…
}
Nel test unitario vorrei sbarazzarmi della rete, ad es. sostituire l'oggetto NSURLConnection
con un mock. Come posso farlo?
Ho provato a creare una simulazione parziale del NetworkManager
che avrebbe sostituito il metodo buildConnection
con uno stub. Il problema è che le derisioni parziali fatte da OCMock bloccano solo i messaggi provenienti dal mondo esterno - l'invio di buildConnection
da startNetworkSync
invoca il metodo originale, non lo stub.
Ho anche provato a patchare la classe NetworkManager
tramite una categoria. Funziona, posso facilmente sostituire il metodo buildConnection
con un altro codice e sostituire il vero NSURLConnection
con uno stub. Il problema è che non ho trovato un modo semplice per ottenere la connessione testata nel test: la connessione è una parte privata del NetworkManager
.
Quindi potrei sottoclassare NetworkManager
, sovrascrivere il metodo buildConnection
e aggiungere una variabile di istanza più un accessor per la connessione creata. Questo sembra molto codice, tuttavia.
Come lo risolveresti? Sto cercando una soluzione che mantenga pulito il design della classe NetworkManager
e non richieda molta magia né molto codice nel test.
Soluzione
Questo è il tipo di cosa che l'iniezione di dipendenza è progettata per risolvere; se si utilizza startNetworkSyncWithConnection: (NSURLConnection *)
invece è possibile testare facilmente il metodo con una connessione fittizia. Se non desideri modificare l'API per i tuoi clienti, potresti persino mantenere startNetworkSync
come wrapper che non fa altro che chiamare quel nuovo metodo con [self buildConnection]
come argomento.
Altri suggerimenti
Ho modificato OCMock per supportare simulazioni parziali reali, consultare il repo su GitHub .
Un'altra soluzione che ho usato di recente è quella di astrarre completamente l'interfaccia di rete. Se la classe necessita di alcuni dati dalla rete, probabilmente interagisce con alcuni servizi server che possono essere esplicitamente modellati come protocollo:
@protocol SomeNetworkService
- (NSArray*) allAvailableFoos;
- (void) insertNewFoo: (Foo*) foo;
@end
E poi avrai una vera implementazione HTTP e una di prova. Ciò significa più lavoro, ma anche una migliore testabilità. I test sono meno fragili e molto più convenienti, poiché il livello della rete di test può fare qualsiasi cosa tu abbia bisogno.