2012-04-12 9 views
10

Sto provando a eseguire alcune prove di base del codice del tipo di concetto per un nuovo progetto mvc3. Stiamo usando Moq con RavenDB.Mock IRavenQueryable con un'espressione Where() aggiunta

Azione:

public ActionResult Index(string id) 
{ 
    var model = DocumentSession.Query<FinancialTransaction>() 
     .Where(f => f.ResponsibleBusinessId == id); 
    return View(model); 
} 

prova:

private readonly Fixture _fixture = new Fixture(); 

[Test] 
public void Index_Action_Returns_List_Of_FinancialTransactions_For_Business([Random(0, 50, 5)]int numberOfTransactionsToCreate) 
{ 
    // Arrange 
    var session = new Mock<IDocumentSession>(); 
    var financialController = new FinancialController { DocumentSession = session.Object }; 

    var businessId = _fixture.CreateAnonymous<string>(); 
    var transactions = _fixture.Build<FinancialTransaction>() 
     .With(f => f.ResponsibleBusinessId, businessId) 
     .CreateMany(numberOfTransactionsToCreate); 

    // Mock 
    var ravenQueryableMock = new Mock<IRavenQueryable<FinancialTransaction>>(); 
    ravenQueryableMock.Setup(x => x.GetEnumerator()).Returns(transactions.GetEnumerator); 
    ravenQueryableMock.Setup(x => x.Customize(It.IsAny<Action<Object>>()).GetEnumerator()).Returns(() => transactions.GetEnumerator()); 

    session.Setup(s => s.Query<FinancialTransaction>()).Returns(ravenQueryableMock.Object).Verifiable(); 

    // Act 
    var actual = financialController.Index(businessId) as ViewResult; 

    // Assert 
    Assert.IsNotNull(actual); 
    Assert.That(actual.Model, Is.InstanceOf<List<FinancialTransaction>>()); 

    var result = actual.Model as List<FinancialTransaction>; 
    Assert.That(result.Count, Is.EqualTo(numberOfTransactionsToCreate)); 
    session.VerifyAll(); 
} 

Sembrerebbe che il problema è nella .Dove (f => f.ResponsibleBusinessId == id). Dal falso IRavenQueryable, sto restituendo un elenco di FinancialTransactions, quindi si penserebbe che .Where() filtrerebbe in base a ciò. Ma dal momento che è IQueryable, immagino che stia cercando di eseguire l'espressione tutto come una volta, quando enumera.

Per verificare, ho cambiato la query di azione per questo:

var model = DocumentSession.Query<FinancialTransaction>() 
    .ToList() 
    .Where(f => f.ResponsibleBusinessId == id); 

Ciò lasciar passare di prova, tuttavia, non è l'ideale, in quanto questo significa che sta andando a elencare tutti i record, poi filtrare.

C'è un modo per far funzionare Moq con questo?

+2

Come idea lato, invece di scherno fuori RavenDB, hai pensato di usare la incorporato nella versione di memoria? Prendere in giro con i metodi di estensione Linq è (davvero) orribile da gestire, e sono andato con RavenDB perché non c'è bisogno di prendere in giro il database. – Rangoric

+0

Sono con @Rangoric - non c'è bisogno di prendere in giro le funzioni 'IDocumentSession' e' IDocumentStore' (o di deridere qualsiasi Ravendb) quando RavenDb ha un 'EmbeddedDocumentStore'. Amico, vai su http://jabbr.net/#/rooms/RavenDB e ci metteremo tutti a chiacchierare perché/perché no. –

+2

Va bene - tranne che sto vedendo RavenDB impiegare troppo tempo per iniziare i miei test unitari - dicendo che "È progettato per non essere deriso" non è mai la risposta giusta. – Ronnie

risposta

7

Come menzionato nei commenti, nei test non si dovrebbe prendere in giro l'API RavenDB.

RavenDB ha un eccellente supporto per unit test, grazie alla modalità InMemory:

[Test] 
public void MyTest() 
{ 
    using (var documentStore = new EmbeddableDocumentStore { RunInMemory = true }) 
    { 
     documentStore.Initialize(); 

     using (var session = documentStore.OpenSession()) 
     { 
      // test 
     } 
    } 
} 
+6

La mia macchina impiega quasi 3 secondi per caricare e inizializzare il server integrato Raven. Questa è una quantità inaccettabile di tempo da aggiungere ai test unitari. –

+0

@MikeScott 3 secondi non suona correttamente. Quali sono le specifiche della tua macchina? –

+0

Quanto tempo ci vuole per il tuo? L'hai mai superato in un test unitario, inclusa l'esecuzione delle attività di creazione dell'indice? Win7/64 su un Intel Core 2 Duo E4600 a 2,4 GHz, 4 GB di RAM. –

3

Come altri hanno detto, se è possibile ottenere da con la modalità in-memory/embedded è grande per integrazione test . Ma non è abbastanza veloce o facile per il test delle unità, a mio parere.

Ho trovato un blog post di Sam Ritchie che offre un 'falso' (wrapper su un LINQ standard IQueryable) per un IRavenQueryable per casi come questo. Il suo è un po 'sorpassato, poiché le versioni più recenti di Raven (attualmente 2.5) offrono alcuni metodi aggiuntivi sull'interfaccia IRavenQueryable. Al momento non uso questi nuovi metodi (, AddQueryInput, Spatial), quindi ho appena lasciato pigramente NotImplementedException nel codice qui sotto, per ora.

Vedere Sam's post per il codice originale su cui ho basato questo e per esempi di utilizzo.

public class FakeRavenQueryable<T> : IRavenQueryable<T> { 
    private readonly IQueryable<T> source; 

    public FakeRavenQueryable(IQueryable<T> source, RavenQueryStatistics stats = null) { 
     this.source = source; 
     this.QueryStatistics = stats; 
    } 

    public RavenQueryStatistics QueryStatistics { get; set; } 

    public Type ElementType { 
     get { return typeof(T); } 
    } 

    public Expression Expression { 
     get { return this.source.Expression; } 
    } 

    public IQueryProvider Provider { 
     get { return new FakeRavenQueryProvider(this.source, this.QueryStatistics); } 
    } 

    public IRavenQueryable<T> Customize(Action<IDocumentQueryCustomization> action) { 
     return this; 
    } 

    public IRavenQueryable<TResult> TransformWith<TTransformer, TResult>() where TTransformer : AbstractTransformerCreationTask, new() { 
     throw new NotImplementedException(); 
    } 

    public IRavenQueryable<T> AddQueryInput(string name, RavenJToken value) { 
     throw new NotImplementedException(); 
    } 

    public IRavenQueryable<T> Spatial(Expression<Func<T, object>> path, Func<SpatialCriteriaFactory, SpatialCriteria> clause) { 
     throw new NotImplementedException(); 
    } 

    public IRavenQueryable<T> Statistics(out RavenQueryStatistics stats) { 
     stats = this.QueryStatistics; 
     return this; 
    } 

    public IEnumerator<T> GetEnumerator() { 
     return this.source.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() { 
     return this.source.GetEnumerator(); 
    } 
} 

public class FakeRavenQueryProvider : IQueryProvider { 
    private readonly IQueryable source; 

    private readonly RavenQueryStatistics stats; 

    public FakeRavenQueryProvider(IQueryable source, RavenQueryStatistics stats = null) { 
     this.source = source; 
     this.stats = stats; 
    } 

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression) { 
     return new FakeRavenQueryable<TElement>(this.source.Provider.CreateQuery<TElement>(expression), this.stats); 
    } 

    public IQueryable CreateQuery(Expression expression) { 
     var type = typeof(FakeRavenQueryable<>).MakeGenericType(expression.Type); 
     return (IQueryable)Activator.CreateInstance(type, this.source.Provider.CreateQuery(expression), this.stats); 
    } 

    public TResult Execute<TResult>(Expression expression) { 
     return this.source.Provider.Execute<TResult>(expression); 
    } 

    public object Execute(Expression expression) { 
     return this.source.Provider.Execute(expression); 
    } 
} 
+0

I test unitari che sono in realtà test di integrazione rappresentano un problema reale. I test unitari dovrebbero essere eseguiti nel più breve tempo possibile per rimanere utilizzabili - e utilizzati - per un lungo periodo di tempo. – utunga

+0

@utunga Non sono sicuro di cosa stai cercando di dire. Se vuoi iniziare una discussione sui test unitari e sui test di integrazione, dovrebbe essere probabilmente in chat o in una domanda su programmers.stackexchange.com. Se questo non è ciò di cui stai parlando, e ha qualcosa a che fare con questa risposta in particolare, ti preghiamo di chiarire. –

+2

scusa ... quello che intendevo dire in realtà è "questa risposta dovrebbe essere al top perché i test di unità realmente dovrebbero essere progettati per essere eseguiti in breve tempo ..". Non per entrare nell'intera unit test vs integration test thing (e notando che Ayende sembra aver cambiato la sua posizione nei test di integrazione e lontano dai test unitari oltre a finte o falsi) ma come la guardo, mentre sto bene con la gente facendo tutto ciò che desiderano a questo proposito, la domanda era chiedere a Moq, quindi quella persona è dopo un (vero) test di unità di misura - quindi la tua risposta dovrebbe essere preferita (anche se si tratta di un falso non di una finta). – utunga