2012-04-25 3 views
12

Sto utilizzando Code First per associare le classi a un database esistente. Ho bisogno di un metodo per testare queste mappature, che sono un mix di convenzioni, attributi e fluenti.Come posso testare i primi mapping di Entity Framework Code?

Al test di unità, devo confermare che le proprietà delle classi si associano ai nomi di tabella e colonna corretti nel database. Questo test deve essere eseguito rispetto al contesto e dovrebbe coprire prima tutte le opzioni di configurazione per il codice.

A un livello molto alto, mi piacerebbe essere alla ricerca di affermare qualcosa di simile (pseudo-codice):

Assert.IsTrue(context.TableFor<Widget>().IsNamed("tbl_Widget")); 
Assert.IsTrue(context.ColumnFor<Widget>(w => w.Property).IsNamed("WidgetProperty")); 
+1

Beh non come EF, se possibile valutare la possibilità di utilizzare NHibernate, con NHibernate puoi testare le tue mappature davvero facili – Jupaol

+0

@Jupaol buono a sapersi, usiamo sia EF che NH e abbiamo i nostri problemi con entrambi – STW

risposta

0

L'unico modo che posso pensare di coprire ogni possibile opzione sarebbe quella di utilizzare l'Entity Framework Power Tools per pre-compilare le viste di DbContext e probabilmente utilizzare una combinazione di reflection su quel tipo generato e RegEx sul codice generato stesso per verificare che tutto sia mappato nel modo desiderato. Mi sembra abbastanza doloroso.

Un'altra cosa che viene in mente è creare una facciata attorno a DbModelBuilder per intercettare e controllare tutto ciò che passa attraverso di esso, ma non so se questo gestirà le cose basate sulla convenzione. Sembra anche doloroso.

Come alternativa meno completa, ma molto più semplice, probabilmente è possibile eliminarne gran parte passando alla mappatura basata sugli attributi laddove possibile. Ciò consentirebbe di creare una classe di test di base, per esempio, ModelTesting <TEntity>, che include un paio di metodi di prova che utilizzano riflessione per verificare che TEntity ha:

  • Un unico TableAttribute.
  • Ogni proprietà ha un singolo ColumnAttribute o NotMappedAttribute.
  • Almeno una proprietà con un KeyAttribute.
  • Ogni tipo di proprietà viene mappata su un tipo di database compatibile.

Si potrebbe persino arrivare a forzare una convenzione di denominazione basata sui nomi delle proprietà e della classe (con un avvertimento per i tipi di tabella per gerarchia). Sarebbe anche possibile controllare anche le mappature delle chiavi esterne. Questa è una classe base di sola scrittura che puoi ricavare da una sola volta per ognuno dei tuoi tipi di modello e cogliere la maggior parte dei tuoi errori (beh, in ogni caso cattura la maggior parte dei miei).

Tutto ciò che non può essere rappresentato da attributi, come l'eredità TPH e così via, diventa un po 'più difficile. Un test di integrazione che attiva DbContext e fa un FirstOrDefault sul Set <TEntity>() probabilmente copre la maggior parte di quelle basi, supponendo che DbContext non stia generando il database per te.

+0

Grazie per l'input Sean! Sto considerando approcci meno esaurienti come l'utilizzo di un singolo metodo di configurazione e daremo un'occhiata agli utensili elettrici EF, non ne ero a conoscenza. – STW

0

Se hai scritto un metodo

public static string ToMappingString(this Widget obj) 

Poi si potrebbe facilmente prove tramite prove di omologazione (www.approvaltests.com o NuGet)

C'è un video qui: http://www.youtube.com/watch?v=vKLUycNLhgc

Tuttavia, se si sta cercando di verificare "I miei oggetti salvare e retrive stessi" allora questo è un posto perfetto di "Teoria Based Testing"

Teoria test basato la maggior parte di unit test assumere la forma di

Given A,B expect C 

Teoria test based è

Given A,B expect Theory 

La bellezza di questo è che non c'è bisogno di preoccuparsi di quale forma particolare Un introito & B dal momento che non c'è bisogno di sapere C, in modo che qualsiasi generatore casuale funzionerà.

Esempio 1: Test aggiungere e sottrarre metodi

Normalmente si dovrebbe avere cose come

Assert.AreEqual(5, Add(2,3)); 
Assert.AreEqual(9, Add(10,-1)); 
Assert.AreEqual(10, Add(5,5)); 
Assert.AreEqual(7, Subtract(10,3)); 

Tuttavia, se hai scritto una prova teorica sarebbe simile

for(int i = 1; i < 100; i++) 
{ 
    int a = random.Next(); 
    int b = random.Next(); 
    Assert.AreEqual(a, Subtract(Add(a,b),b, string.Format("Failed for [a,b] = [{0},{1}], a,b));   
} 

Ora che Comprendi i test basati sulla teoria, la teoria che stai cercando di testare è

Given Model A 
When A is stored to the database, and retrieved the resulting object is equal to A 
1

Un'altra idea da considerare è l'utilizzo di Linq e ToString().

Per eaxample questo:

context.Widget.Select(c => c.Property).ToString() 

comporterà questo per provider di SQL Server:

"SELECT [Var_3].[WidgetProperty] AS [WidgetProperty] FROM [dbo].[Widget]..." 

Ora abbiamo potuto nascondere tutto in un metodo di estensione che e analizza risultante SQL sarebbe risultato quasi come il tuo pseudo-codice:

Assert.IsTrue(context.Widgets.GetSqlColumnNameFor(w => w.Property).IsNamed("WidgetProperty")); 

Bozza per estensione:

public string GetSqlColumnNameFor<TSource>(this DbSet<T> source, Expression<Func<TSource, TResult>> selector) 
{ 
    var sql = source.Select(selector).ToString(); 

    var columnName = sql... // TODO : Some regex parsing 

    return 
     columnName; 
} 

Similariamente è possibile creare GetSqlTableNameFor().

UPDATE: ho deciso di cercare alcuni parser dedica SQL, quindi questa soluzione è più generico, ovviamente, c'è una cosa del genere per NET:

http://www.dpriver.com/blog/list-of-demos-illustrate-how-to-use-general-sql-parser/generate-internal-query-parse-tree-in-xml-for-further-processing/