2010-05-01 20 views
27

Kłaniam się interfejsem mojego repozytorium i nie jestem pewien jak skonfigurować metodę, która pobiera wyrażenie i zwraca obiekt? Używam Moq i NUnitMoq.Mock - jak ustawić metodę, która przyjmuje wyrażenie

Interfejs:

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

test z IQueryable już zainstalowane, ale nie wiem jak ustawić T Single:

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); 
} 

Odpowiedz

37

Można ustawić go jak to:

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

Jednak pojawia się przeciwko jednej z wad Moq. Zamiast używać It.IsAny, chciałbyś wstawić rzeczywiste wyrażenie, ale Moq nie obsługuje ustawiania metod, które przyjmują wyrażenia z określonymi wyrażeniami (jest to trudna do implementacji funkcja). Trudność wynika z konieczności ustalenia, czy dwa wyrażenia są równoważne.

Tak więc w teście możesz przekazać dowolnąExpression<Func<Page,bool>>, a to cofnie wszystko, co skonfigurowałeś, aby powrócić. Wartość testu jest trochę rozcieńczona.

+1

Dzięki za odpowiedź. Otrzymuję błąd z powyższym kodem: Błąd Argument "1": nie można przekonwertować z "grupy metod" na "System.Linq.Expressions.Expression > – Paul

+0

@Paul: Przepraszam, upuściłem '()'. Wypróbuj najnowszą wersję i powinna działać. –

+0

Dzięki za odpowiedź, która zadziałała, nie idealna jak wspomniałeś, ale działa! Dzięki jeszcze raz. – Paul

6

Wywołanie .Returns zwraca wynik wyrażenia względem zmiennej allPages.

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

Korzystanie Moq na It.IsAny<> bez .CallBack sił na pisanie kodu, który nie jest objęty testu. Zamiast tego pozwala na przejście wszelkich zapytań/wyrażeń, co sprawia, że ​​twoja próbka jest zasadniczo bezużyteczna z perspektywy testowania jednostkowego.

Rozwiązanie: Musisz użyć funkcji oddzwaniania, aby przetestować wyrażenie LUB musisz ograniczyć próbę lepiej. Tak czy inaczej jest brudny i trudny. Zajmowałem się tym problemem tak długo, jak długo praktykuję TDD. W końcu zrzuciłem klasę pomocników, aby uczynić ją bardziej wyrazistą i mniej niechlujną. Oto przykład jednego możliwego rezultatu końcowego:

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

oto artykuł blog, który mówi o tym i daje kod źródłowy: http://awkwardcoder.com/2013/04/24/constraining-mocks-with-expression-arguments/

+0

Bardzo przydatny, wielki wkład. – Paul

4

Znalazłem że It.Is<T> powinien być stosowany w miejsce It.IsAny<T> dłużej dokładne wyniki.

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); 
Powiązane problemy