Supponiamo che sto definendo una funzione Haskell f (sia pura che un'azione) e da qualche parte all'interno di f chiamo la funzione g. Ad esempio:Come fingere di provare in Haskell?
f = ...
g someParms
...
Come si sostituisce la funzione g con una versione fittizia per il test dell'unità?
Se stavo lavorando in Java, g sarebbe un metodo sulla classe SomeServiceImpl
che implementa l'interfaccia SomeService
. Quindi, userei l'iniezione di dipendenza per dire a f o usare SomeServiceImpl
o MockSomeServiceImpl
. Non sono sicuro di come farlo in Haskell.
è il modo migliore per farlo di introdurre un tipo di classe someservice:
class SomeService a where
g :: a -> typeOfSomeParms -> gReturnType
data SomeServiceImpl = SomeServiceImpl
data MockSomeServiceImpl = MockSomeServiceImpl
instance SomeService SomeServiceImpl where
g _ someParms = ... -- real implementation of g
instance SomeService MockSomeServiceImpl where
g _ someParms = ... -- mock implementation of g
Poi, ridefinire F come segue:
f someService ... = ...
g someService someParms
...
Sembra che questo dovrebbe funzionare, ma sono semplicemente imparando Haskell e chiedendosi se questo è il modo migliore per farlo? Più in generale, mi piace l'idea dell'iniezione di dipendenza non solo per il derisione, ma anche per rendere il codice più personalizzabile e riutilizzabile. In generale, mi piace l'idea di non essere bloccato in una singola implementazione per nessuno dei servizi utilizzati da un pezzo di codice. Sarebbe considerata una buona idea usare il trucco sopra in modo estensivo nel codice per ottenere i benefici dell'iniezione di dipendenza?
EDIT:
Prendiamo questo un ulteriore passo avanti. Supponiamo che io abbia una serie di funzioni a, b, c, d, e ed f in un modulo che tutti devono essere in grado di fare riferimento alle funzioni g, h, i e j da un modulo diverso. E supponiamo di voler essere in grado di simulare le funzioni g, h, i e j. Potrei chiaramente passare le 4 funzioni come parametri ad a-f, ma è un po 'difficile aggiungere i 4 parametri a tutte le funzioni. Inoltre, se avessi mai avuto bisogno di cambiare l'implementazione di uno qualsiasi di a-f per chiamare ancora un altro metodo, avrei bisogno di cambiare la sua firma, che potrebbe creare un brutto esercizio di refactoring.
Eventuali trucchi per rendere questo tipo di situazione funzionano facilmente? Ad esempio, in Java, potrei costruire un oggetto con tutti i suoi servizi esterni. Il costruttore memorizzerebbe i servizi nelle variabili membro. Quindi, qualsiasi metodo potrebbe accedere a tali servizi tramite le variabili membro. Quindi, man mano che i metodi vengono aggiunti ai servizi, nessuna delle firme dei metodi cambia. E se sono necessari nuovi servizi, cambia solo la firma del metodo del costruttore.
perché uno vorrebbe prendere in giro le funzioni pure? – yairchu
Buon punto, yairchu. Probabilmente potresti solo prendere in giro le azioni. –
@yairchu Lo farei per motivi di efficienza. Se i test prendono x o 100 volte il tempo è estremamente importante per la produttività. Quindi vorrei dire che factorial 100000000 = a scopo di test (quindi come input/mock a un altro test). Ma quel fattoriale 100000000 = potrebbe essere un test di per sé quando il test runner gira sul modulo fattoriale. –
user239558