Soru Moq ile IQueryable uygulayan sınıfları alay


IQueryable'i uygulayan bir nesneyle dalga geçmeye çalışırken bir akşam geçirdim:

public interface IRepo<T> : IQueryable<T>
{
}

Gelebildiğim en iyi şey şunun gibi:

var items = new Item[] {}.AsQueryable();

var repo = new Mock<IRepo>();
repo.Setup(r => r.GetEnumerator()).Returns(items.GetEnumerator());
repo.Setup(r => r.Provider).Returns(items.Provider);
repo.Setup(r => r.ElementType).Returns(items.ElementType);
repo.Setup(r => r.Expression).Returns(items.Expression);

Aynısını yapmak için daha özlü bir yolu var mı? IRepo'da IQueryable değerini döndüren bir özellik / yöntem ortaya çıkarmak daha kolay olurdu.

repo.Setup(r => r.GetItems()).Returns(new Items[]{ }.AsQueryable());

Ama yapmak istediğim bu değil =)


32
2017-08-13 04:09


Menşei




Cevaplar:


Bu yeni bir şey değil, sadece bunu yapmanın daha temiz bir yolu. Ayrıca, deponun kendisinin de IQueryable olduğu depolarım var, bu yüzden aynı şeye ihtiyacım vardı. Temel olarak kodunuzu test projemin kök seviyesinde böyle bir uzantı yöntemine sokarak tüm testlerde kullanılabilir olmasını sağladım:

public static class MockExtensions
{
    public static void SetupIQueryable<T>(this Mock<T> mock, IQueryable queryable)
        where T: class, IQueryable
    {
        mock.Setup(r => r.GetEnumerator()).Returns(queryable.GetEnumerator());
        mock.Setup(r => r.Provider).Returns(queryable.Provider);
        mock.Setup(r => r.ElementType).Returns(queryable.ElementType);
        mock.Setup(r => r.Expression).Returns(queryable.Expression);
    }
}

Bu temelde yeniden kullanılabilirlik sunar, çünkü bunu birkaç testte yapmak isteyeceğinizden ve her testte amaç net ve karışıklığı en aza indirir. :)


38
2017-12-16 11:24



Bu fikri beğeniyorum. - LeffeBrune
İyi fikir. Aynı şeyi uyguladık, ancak repo koleksiyonunu birkaç kez sorguladığımız durumlar için Rob'un sayım sıfırlamasını da dahil ettik. - MCattle


Rune'un cevabı müthiş ve bana nasıl yapılacağını anlatan zaman kazandı. Small gotcha, IQueryable üzerinde iki kez IQueryable uzantı yöntemini çağırırsanız (ör. ToList ()), ikinci kez hiçbir sonuç almazsınız. Bunun nedeni, sayımın sonunda olduğu ve sıfırlanması gerektiğidir. Rhinomocks'ı kullanarak GetEnumerator için uygulamayı değiştirdim:

mock.Stub(r => r.GetEnumerator()).Do((Func<IEnumerator<T>>) (() => { 
    var enumerator = queryable.GetEnumerator();
    enumerator.Reset();
    return enumerator;
}));

Umarım birileri zaman kaybeder.


7
2018-04-16 14:01



Güzel bir Rob, teşekkürler. Bu sorunu yaşadığım için Moq için eşdeğer elde etmeye bakacağım. - The Senator
@ senatör lütfen Moq çözümü gönderin - OzBob
@OzBob bir Moq güncellemesini takip ettikten sonra, orijinal çözümüm numaralayıcıyı sıfırlama gereği duymadan çalıştı. Hala modern bir Moq versiyonu ile bu sıkıntıya şahit olduğunuzu onaylayabilir misiniz? Eğer öyleyse, bunu kanıtlayan birim testimi takip edeceğim ve başka bir yol var! - The Senator
Teşekkürler @Rob! Kaybınızı bulana kadar kaybolacak bir günüm var. - Delorian
Bu da benim için pastırma kurtardı! mockSet.As<IQueryable<MyEntity>>().Setup(m => m.GetEnumerator()).Returns(() => { var enumerator = queryable.GetEnumerator(); enumerator.Reset(); return enumerator; }); - Larsbj


Rune'un cevabını seviyorum. İşte genel bir IQueryable sürümü:

public static void SetupIQueryable<TRepository, TEntity>(this Mock<TRepository> mock, IQueryable<TEntity> queryable)
   where TRepository : class, IQueryable<TEntity>
{
    mock.Setup(r => r.GetEnumerator()).Returns(queryable.GetEnumerator());
    mock.Setup(r => r.Provider).Returns(queryable.Provider);
    mock.Setup(r => r.ElementType).Returns(queryable.ElementType);
    mock.Setup(r => r.Expression).Returns(queryable.Expression);
}

6
2018-03-17 10:57





Bence bu, Moq ile yapabileceğiniz en iyi şey hakkında. Bence daha okunabilir bir seçenek kendi kendine yuvarlamak olurdu. FakeRepo<T> gelen System.Linq.EnumerableQuery<T>:

public class FakeRepo<T> : EnumerableQuery<T>, IRepo<T>
{
    public FakeRepo(IEnumerable<T> items) : base(items) { }
}

Güncelleştirme: Bunu alay ederek çekebilirsin. EnumerableQuery<T> sonra kullanarak As<T>():

var items = new Item[0];

var repo = new Mock<EnumerableQuery<Item>(items).As<IRepo>();

2
2017-08-13 09:32





Bende aynı sorun vardı. Bu satırı değiştirerek düzeltdim

mock.Setup(r => r.GetEnumerator()).Returns(queryable.GetEnumerator());

için

mock.Setup(r => r.GetEnumerator()).Returns(queryable.GetEnumerator);

Umarım burada ek yorum gerekli değildir.


0
2018-02-11 11:27