2010-05-01 10 views
27

Sto simulando l'interfaccia del mio repository e non sono sicuro di come impostare un metodo che accetta un'espressione e restituisce un oggetto? Sto usando Moq e NUnitMoq.Mock <T> - come impostare un metodo che prende un'espressione

Interfaccia:

public interface IReadOnlyRepository : IDisposable 
{ 
    IQueryable<T> All<T>() where T : class; 
    T Single<T>(Expression<Func<T, bool>> expression) where T : class; 
} 

di prova con IQueryable già messa a punto, ma non so come impostare il T unico:

private Moq.Mock<IReadOnlyRepository> _mockRepos; 
private AdminController _controller; 
[SetUp] 
public void SetUp() 
{ 
    var allPages = new List<Page>(); 
    for (var i = 0; i < 10; i++) 
    { 
     allPages.Add(new Page { Id = i, Title = "Page Title " + i, Slug = "Page-Title-" + i, Content = "Page " + i + " on page content." }); 
    } 
    _mockRepos = new Moq.Mock<IReadOnlyRepository>(); 
    _mockRepos.Setup(x => x.All<Page>()).Returns(allPages.AsQueryable()); 
    //Not sure what to do here??? 
    _mockRepos.Setup(x => x.Single<Page>() 
    //---- 
    _controller = new AdminController(_mockRepos.Object); 
} 

risposta

37

È possibile configurarlo come this:

_mockRepos.Setup(x => x.Single<Page>(It.IsAny<Expression<Func<Page, bool>>>()))//.Returns etc...; 

Tuttavia si sta affrontando uno dei difetti di Moq. Dovresti inserire un'espressione reale invece di usare It.IsAny, ma Moq non supporta l'impostazione di metodi che accettano espressioni con espressioni specifiche (è una caratteristica difficile da implementare). La difficoltà deriva dal dover capire se due espressioni sono equivalenti.

Quindi nel test è possibile passare in qualsiasiExpression<Func<Page,bool>> e passerà indietro tutto ciò che è stato configurato per riprendere. Il valore del test è un po 'diluito.

+1

Grazie per la risposta. Ricevo l'errore con il codice sopra: Errore Argomento '1': impossibile convertire da 'metodo gruppo' a 'System.Linq.Expressions.Expression > – Paul

+0

@Paul: scusa, ho lasciato cadere '()'. Prova con l'ultima versione e dovrebbe funzionare. –

+0

Grazie per la risposta, che ha funzionato, non ideale come hai detto tu, ma funziona! Grazie ancora. – Paul

6

La chiamata .Returns restituisce il risultato dell'espressione sulla variabile allPages.

_mockRepos.Setup(x => x.Single<Page>(It.IsAny<Expression<Func<Page, bool>>>())) 
    .Returns((Expression<Func<Page, bool>> predicate) => allPages.Where(predicate)); 
1

Usando del It.IsAny<> senza un .CallBack forze di scrivere codice che non è coperto dalla vostra prova Moq. Invece, permette a qualsiasi query/espressione di passare attraverso, rendendo il tuo finto praticamente inutile da una prospettiva di testing unitario.

La soluzione: è necessario utilizzare una richiamata per testare l'espressione OPPURE è necessario limitare meglio la simulazione. In entrambi i casi è disordinato e difficile. Ho affrontato questo problema fino a quando ho praticato TDD. Alla fine ho riunito una classe di aiuto per renderla molto più espressiva e meno disordinata. Ecco un esempio di un possibile risultato finale:

mockPeopleRepository 
    .Setup(x => x.Find(ThatHas.AnExpressionFor<Person>() 
    .ThatMatches(correctPerson) 
    .And().ThatDoesNotMatch(deletedPerson) 
    .Build())) 
    .Returns(_expectedListOfPeople); 

Ecco l'articolo del blog che ne parla e dà il codice sorgente: http://awkwardcoder.com/2013/04/24/constraining-mocks-with-expression-arguments/

+0

Molto utile, ottimo contributo. – Paul

4

Ho scoperto che It.Is<T> dovrebbe essere usato al posto di It.IsAny<T> per più risultati accurati.

Page expectedPage = new Page {Id = 12, Title = "Some Title"}; 
_mockRepos.Setup(x => x.Single<Page>(It.Is<Expression<Func<Page, bool>>>(u => u.Compile().Invoke(expectedPage)))) 
         .Returns(() => expectedPage); 
Problemi correlati