Önbelleği yoğun kullanan birim test yöntemleri için en iyi uygulamalar?


17

Saklamak ve (filtreleme ile) nesneleri ve nesne listelerini önbellekten almak iş mantık yöntemleri bir dizi var.

Düşünmek

IList<TObject> AllFromCache() { ... }

TObject FetchById(guid id) { ... }

IList<TObject> FilterByPropertry(int property) { ... }

Fetch..ve Filter..çağırır AllFromCacheorada değilse önbellek ve getiri doldurmak ve eğer sadece ondan dönecekti hangi.

Genellikle bunları test eden birimden utanıyorum. Bu tür yapıya karşı Birim Testi için en iyi uygulamalar nelerdir?

TestInitialize'da önbellek doldurmayı ve TestCleanup'ta kaldırmayı düşündüm, ancak bu bana doğru gelmiyor (tho olabilir).

Yanıtlar:


18

Gerçek Birim Testleri istiyorsanız, önbelleği taklit etmeniz gerekir: önbellekle aynı arabirimi uygulayan bir sahte nesne yazın, ancak önbellek olmak yerine aldığı çağrıları izler ve her zaman gerçek olanı döndürür önbellek, test senaryosuna göre iade edilmelidir.

Tabii ki önbellek de birim testine ihtiyaç duyar, bunun için bağlı olduğu her şeyi alay etmelisiniz, vb.

Gerçek önbellek nesnesini kullanarak ancak bunu bilinen bir duruma başlatmak ve testten sonra temizlemek, tanımladığınız şey daha çok bir entegrasyon testi gibidir, çünkü birkaç üniteyi konserde test ediyorsunuz.


+1 bu meydan okurcasına en iyi yaklaşımdır. Önbellek beklediğiniz gibi çalıştığını doğrulamak için mantık ve sonra entegrasyon testi için birim testi.
Tom Squires

10

Tek Sorumluluk Prensibi burada iyi arkadaşı.

Her şeyden önce, AllFromCache () 'i bir havuz sınıfına taşıyın ve GetAll () olarak adlandırın. Önbellekten alması deponun bir uygulama detayıdır ve çağıran kod tarafından bilinmemelidir.

Bu, filtreleme sınıfınızı test etmeyi kolay ve kolay hale getirir. Artık nereden aldığınız umurunda değil.

İkinci olarak, verileri veritabanından (veya her yerde) bir önbellek sarmalayıcısına alan sınıfı sarın.

AOP bunun için iyi bir tekniktir. Çok iyi olduğu birkaç şeyden biri.

PostSharp gibi araçları kullanarak , seçilen bir özellik ile işaretlenmiş herhangi bir yöntemin önbelleğe alınmasını sağlayacak şekilde ayarlayabilirsiniz. Ancak, önbelleğe aldığınız tek şey buysa, bir AOP çerçevesine sahip olmak kadar ileri gitmenize gerek yoktur. Aynı arabirimi kullanan ve bunu çağıran sınıfa enjekte eden bir Havuz ve Önbellek Sarıcıya sahip olmanız yeterlidir.

Örneğin.

public class ProductManager
{
    private IProductRepository ProductRepository { get; set; }

    public ProductManager
    {
        ProductRepository = productRepository;
    }

    Product FetchById(guid id) { ... }

    IList<Product> FilterByPropertry(int property) { ... }
}

public interface IProductRepository
{
    IList<Product> GetAll();
}

public class SqlProductRepository : IProductRepository
{
    public IList<Product> GetAll()
    {
        // DB Connection, fetch
    }
}

public class CachedProductRepository : IProductRepository
{
    private IProductRepository ProductRepository { get; set; }

    public CachedProductRepository (IProductRepository productRepository)
    {
        ProductRepository = productRepository;
    }

    public IList<Product> GetAll()
    {
        // Check cache, if exists then return, 
        // if not then call GetAll() on inner repository
    }
}

Havuz uygulama bilgilerini ProductManager'dan nasıl kaldırdığınızı gördünüz mü? Ayrıca, veri çıkarmayı işleyen bir sınıfa, veri almayı işleyen bir sınıfa ve önbelleğe almayı işleyen bir sınıfa sahip olarak Tek Sorumluluk İlkesi'ne nasıl uyduğunuzu görün.

Artık ProductManager'ı bu Depolardan herhangi biriyle başlatabilir ve önbelleğe alabilirsiniz ... ya da edemezsiniz. Bu, daha sonra önbelleğin bir sonucu olduğundan şüphelendiğiniz kafa karıştırıcı bir hata aldığınızda inanılmaz derecede yararlıdır.

productManager = new ProductManager(
                         new SqlProductRepository()
                         );

productManager = new ProductManager(
                         new CachedProductRepository(new SqlProductRepository())
                         );

(Bir IOC kabı kullanıyorsanız, daha da iyi. Nasıl adapte olacağınız açık olmalıdır.)

Ve ProductManager testlerinizde

IProductRepository repo = MockRepository.GenerateStrictMock<IProductRepository>();

Önbelleği test etmeye gerek yok.

Şimdi soru şu: Bu CachedProductRepository'yi test etmeli miyim? Önermiyorum. Önbellek oldukça belirsiz. Çerçeve onunla sizin kontrolünüz dışında olan şeyleri yapar. Mesela, çok fazla dolduğunda bir şeyleri çıkarmak gibi. Bir mavi ayda bir kez başarısız olan testlerle sonuçlanacaksınız ve nedenini asla gerçekten anlamayacaksınız.

Ve yukarıda önerdiğim değişiklikleri yaptıktan sonra, orada test edilecek çok fazla mantık yok. Gerçekten önemli bir test olan filtreleme yöntemi orada olacak ve GetAll () 'ın ayrıntılarından tamamen soyutlanacaktır. GetAll () sadece ... hepsini alır. Bir yerlerden.


ProductManager'da CachedProductRepository kullanıyorsanız ancak SQLProductRepository'de bulunan yöntemleri kullanmak istiyorsanız ne yaparsınız?
Jonathan

@Jonathan: "Sadece aynı arabirimi kullanan bir Havuz ve Önbellek Sarmalayıcısına sahip olun" - aynı arabirime sahiplerse aynı yöntemleri kullanabilirsiniz. Çağıran kodun uygulama hakkında hiçbir şey bilmesi gerekmez.
pdr

3

Senin önerdiğin yaklaşım benim yapacağım şeydi. Açıklamanız göz önüne alındığında, yöntemin sonucu nesnenin önbellekte bulunsun veya bulunmasın aynı olmalıdır: yine de aynı sonucu almalısınız. Her testten önce önbelleği belirli bir şekilde ayarlayarak test etmek kolaydır. Muhtemelen, kılavuzun nullistenen özelliğe sahip olup olmadığı veya istenen nesneye sahip olmaması gibi bazı ek durumlar vardır; bunlar da test edilebilir.

Ayrıca, olabilir o nesne olursa olsun ilk etapta önbellekte bakılmaksızın, sizin yöntemi döndükten sonra önbelleği bulunması beklenmektedir düşünün. Bazı insanlar (kendim dahil) , nasıl elde edeceğinizi değil, arayüzünüzden geri aldığınız şeyleri önemsediğinizi iddia ettiği için bu tartışmalıdır (yani, arayüzün beklendiği gibi çalıştığını, belirli bir uygulamaya sahip olmadığını test edin). Önemli olduğunu düşünürseniz, bunu test etme şansınız var.


1

TestInitialize'da önbellek doldurmayı ve TestCleanup'ta kaldırmayı düşündüm, ancak bu bana doğru gelmiyor

Aslında, bunu yapmanın tek doğru yolu budur. Bu iki işlevin amacı şudur: ön koşulları ayarlamak ve temizlemek. Ön koşullar karşılanmazsa, programınız çalışmayabilir.


0

Son zamanlarda önbellekleme kullanan bazı testler üzerinde çalışıyordum. Önbellekle çalışan sınıfın etrafında bir sarmalayıcı oluşturdum ve sonra bu sarmalayıcının çağrıldığına dair iddiaları vardı.

Bunu esas olarak önbellekle çalışan sınıfın statik olması nedeniyle yaptım.


0

Önbellek mantığını test etmek istediğiniz anlaşılıyor, ancak doldurma mantığı değil. Bu yüzden test etmeniz gerekmeyen şeylerle alay etmenizi öneririm - nüfus.

Kişisel AllFromCache()yöntem önbelleği doldurma ilgilenir ve bu değerlerin bir tedarikçi gibi, başka bir şeye devredilmelidir. Böylece kodunuz

private Supplier<TObject> supplier;

IList<TObject> AllFromCache() {
    if (!cacheInitialized) {
        //whatever logic needed to fill the cache
        cache.putAll(supplier.getValues());
        cacheInitialized = true;
    }

    return  cache.getAll();
}

Şimdi, önceden tanımlanmış bazı değerleri döndürmek için tedarikçiyi test için alay edebilirsiniz. Bu şekilde, gerçek filtrelemeyi ve getirmeyi test edebilir ve nesneleri yüklemezsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.