Il repository stesso non viene solitamente testato?

Mi dispiace, ma sono nuovo per i modelli di repository, unit test e strumenti orm.

Ho fatto ricerche sui test unitari e sul modello di deposito e sono arrivato a delle conclusioni, mi chiedo se ho ragione.

Il modello di repository facilita la sostituzione dell’unità nel controllore che la utilizza, ad esempio, giusto? Perché creare uno stub / falso contesto (in EF) o sessione (in NH) è più difficile, giusto? Il repository stesso non è testato? Perché?

Usando EntityFramework o NHibernate con pattern di repository, se voglio testare i miei repository ho bisogno di fare test di integrazione? Perché se utilizzo una falsa implementazione del mio contesto / sessione non sto facendo veri test? Perché il contesto / sessione stessa è il repository (intendo che implementano la vera logica di Aggiungi, Rimuovi, Modifica, GetById, GetAll, ..)?

Il modello di repository con EF o NH è come un wrapper? (Non solo un wrapper, so che questo è un concetto di importazione del dominio.)

In questo caso, differirei strettamente tra EF e NH e non includerei entrambe le tecnologie nella stessa domanda. Simple NH è più maturo e ha un’architettura che porta al codice che può essere più facilmente testato. Anche in caso di NH puoi semplicemente passare il database a un altro (come SQLite) e funzionerà ancora lo stesso che non deve essere vero in caso di EF, in cui il cambio di database può portare a test di un’applicazione completamente diversa, specialmente se si passa tra database MS e non MS.

Qual è il repository? Vediamo la definizione di Martin Fowler :

Un repository fa da intermediario tra il dominio e i livelli di mapping dei dati, agendo come una raccolta di oggetti di dominio in memoria. Gli oggetti client costruiscono le specifiche di query in modo dichiarativo e li inoltrano al Repository per soddisfazione. Gli oggetti possono essere aggiunti e rimossi dal Repository, come da una semplice raccolta di oggetti, e il codice di mapping incapsulato dal Repository eseguirà le operazioni appropriate dietro le quinte. Concettualmente, un repository incapsula l’insieme di oggetti presenti in un archivio dati e le operazioni eseguite su di essi, fornendo una vista più orientata agli oggetti del livello di persistenza. Repository supporta inoltre l’objective di ottenere una separazione netta e una dipendenza a senso unico tra il dominio e i livelli di mapping dei dati.

Bella definizione Ora pensa allo scopo di DbSet :

  • Funziona come nella raccolta della memoria? Sì, puoi usarlo per ottenere entity framework dal database o usare la proprietà Local per ottenere quadro già caricate.
  • Le specifiche della query del cliente possono essere dichiarate? Sì, si chiama linq-to-entities.
  • Gli oggetti possono essere aggiunti o rimossi dalla collezione? Sì, può.
  • La mapping è incapsulata? Sì.
  • C’è una separazione netta? In termini di logica si. In termini di API no perché l’esposizione di IDbSet al modello di dominio renderà il modello di dominio dipendente dalla tecnologia – EF. È un problema? In teoria sì, per purista sì ma nel 99% non è davvero un problema perché la situazione in cui è necessario modificare l’API è rara e comporta sempre grandi cambiamenti anche se le API sono state correttamente separate.

DbSet è un repository. L’unica differenza tra l’utilizzo diretto di DbSet e il suo inserimento in un repository generico è una separazione. Questo porta alla mia precedente risposta a una domanda simile – Repository generico con EF 4.1 qual è il punto

Ora, qual è lo scopo del repository nella tua applicazione? Ho visto le tue domande precedenti incluso questo in cui hai il tuo BaseRepository costruito sopra il framework Entity. Se si intende seriamente questo come un repository di base che sarà il padre per i repository specializzati che lavorano su root aggregati per il modello di dominio e che espongono metodi specializzati relativi solo a tipi di quadro esposte specifiche allora sì – si sta utilizzando il pattern di repository e ne avete bisogno. Tuttavia, se si dispone solo del contesto e di un set singolo e si chiama questo repository, è probabile che sia stato creato solo il layer ridondante con un valore aggiunto dubbio poiché si tratta solo di un wrapper su DbSet .

Esiste un solo scenario in cui il repository (wrapper DbSet ) ha senso in questo caso:

  • Il wrapper non esporrà mai IQueryable (linq-to-entities)
  • Il wrapper non accetterà mai Expression<> e lo passerà internamente a IQueryalbe (linq-to-entities)

Questo è l’unico scenario che ti offrirà repository completamente discutibili => il tuo livello superiore può essere facilmente testato su unità. Non stai andando ai repository di unit test e non hai intenzione di simulare il contesto usato nei repository. I repository racchiudono l’accesso ai dati e la logica di mapping: gli unici test ragionevoli in caso di repository sono test di integrazione. Qual è il problema di questo scenario? Perderete tutta la potenza di LINQ e dovrete avvolgere / re-implementare alcuni metodi e tipi implementati in EF. Questo tipo di repository è lo stesso utilizzato quando si esegue il wrapping dell’accesso ai dati utilizzando stored procedure.

Se non segui lo scenario, la tua vita sarà molto più facile. Avrete le query definite da LINQ ma non sarete in grado di testare il codice dell’unità perché non c’è alcun falso / falso che valuterà ancora le query come linq-to-entity. Una volta DbSet giro DbSet o IQueryable si utilizzerà linq-to-object che è superset di linq-to-entities. È ansible scrivere facilmente una query che supererà un test su DbSet ma fallirà in fase di esecuzione con un DbSet reale. Ecco di più su questo problema ed ecco l’esempio di query che supererà il test ma fallirà in fase di runtime. In questo caso è necessario utilizzare i test di integrazione (con database reale) per tutti i metodi che utilizzano le query linq-to-entities sopra i repository.

L’ interfaccia del repository appartiene al livello del dominio. Ma l’implementazione appartiene al livello di accesso alle infrastrutture o ai dati. Questa implementazione non viene solitamente testata con il test unitario . Utilizza pesantemente l’API ORM, quindi è estremamente difficile e dispendioso testare il repository da solo. Questo problema non è specifico per i repository: non prendere in giro tipi che non possiedi.

Il repository deve essere testato con test di integrazione , utilizzando l’ORM reale. Nel database di memoria è un approccio molto popolare a questo problema.

… Perché se utilizzo un’implementazione falsa del mio contesto / sessione non sto facendo veri test?

Anche se riuscirai a farcela (cosa di cui dubito davvero nel caso di NHibernate) perderai il tuo tempo. L’interfaccia Session / Context è fuori dal tuo controllo e il tuo test reiterarebbe solo le tue ipotesi su come funzionerà la cosa reale.

Becaushe il contesto / sessione stessa è il repository?

No. Context / Session è l’implementazione del pattern UnitOfWork . Non fa parte del tuo dominio. Questa è l’infrastruttura.

Il modello di repository con EF o NH è solo come un wrapper?

Il repository è un concetto di dominio importante, non è solo un “wrapper”. Proprio come la tua applicazione non è un ‘wrapper’ sul database. Penso che la definizione dell’interfaccia di repository DDD dovrebbe essere basata su Ubiquitous Language il più ansible e non dovrebbe includere parole o tipi correlati a ORM.

È piuttosto comune creare repository che sono solo involucri leggeri dell’accesso ai DB e mettere i metodi di business in un livello di servizio dell’entity framework che dipende dai repository, sì. In questo modo è ansible testare i servizi delle quadro utilizzando repository che eseguono il wrap, ad esempio un DB in memoria.

Quando si esegue il test delle unità, è necessario essere chiari su quale “unità” si sta testando. Prendi in giro cose che sono al di fuori della tua Unità mentre collaudi la tua Unità. Le cose che deridi possono essere testate separatamente.

C’è un altro tipo di test in cui testare pezzi più grandi. Quindi potresti testare il tuo codice + repository + database. C’è un valore in questo, ma non sta testando le stesse cose. In particolare quando si utilizza un database reale è più difficile forzare il codice su alcuni percorsi di errore.

Dovresti testare {il tuo codice + repository} e prendere in giro il database? Dipende dalla complessità e dalla comprovata validità del repository.