11

Birim sınamalarının, kapsayıcısını aramaya güvenmemesine çalışıyorum. Bağımlılıkları için <T>()'u çözünüz.Bağımlılık enjeksiyonunu kolaylaştırmak için parametresiz bir kurucu olmadan bir .NET birimi sınaması

Şu anda AutoFac 2.2.4 kullanarak ve xUnit.NET ve NUnit çalıştı, ancak her ikisi de bu sorunu var ediyorum: Bu nesne

için tanımlanan

yok parametresiz yapıcı

nasıl bu konudaki geçmiş alabilirim? Bunu destekleyecek belirli bir birim test çerçevesi mi, yoksa söz konusu çerçeve nasıl yapılandırılmış?

Bunu yaparken gerekmiyor mu? Ya da test sınıfını, yalnızca bağımlılığı olan kurucuyla çalışacak şekilde ayarlayabilir miyim?

İşte kod bazıları aşağıda verilmiştir:

public class ProductTests : BaseTest 
{ 
    readonly private IProductRepository _repo; 

    public ProductTests(IProductRepository r) 
    { 
     _repo = r; 
    } 

    //working unit tests here with default constructor 
} 

Ben temel sınıf yapıcısı yanlış kabı başlatmak için tercih mü?

public abstract class BaseTest 
{ 
    protected BaseTest() 
    { 
     var builder = new ContainerBuilder(); 
     builder.RegisterType<ProductRepository>().As<IProductRepository>(); 
     builder.Build(); 
    } 
} 
+1

Test sınıfının neden bir kurucuya ihtiyacı var? "Enjeksiyon" ayar yöntemine koyun. –

cevap

11

İlk sorun gerçekten de test çerçevelerinin nasıl tasarlandığından kaynaklanmaktadır. Test örneklerini başlatmak için hepsi parametresiz bir kurucu gerektirir. Ve haklı olarak öyle. Bu çerçevelerle, kurucunun test başlatması için güvenilmemesi gerekir. Bu, SetUp yönteminin amacıdır. Sonuç olarak, test sınıflarının kendisi enjeksiyon için uygun değildir.

Ve IMO, bu kabın bağlı olmayan şekilde testler geliştirmek olmayan bir konu haline gelir. Sonuçta, her bir test sınıfı bir "test altındaki sistem" (SUT) üzerine odaklanmalıdır. Neden kurulum yöntemi bu sistemi doğrudan başlatmaz ve her bağımlılığı sağlar (genellikle sahte olarak)? Bu şekilde, testlerinizden başka bir gereksiz bağımlılığı etkin bir şekilde ortadan kaldırdınız, yani IoC çerçevesi. Benim testlerde IoC çerçeve benim "kapsayıcı testlerinde" olduğunu dahil sadece zaman: Yan not

. Bu testler, konteynerin application or assembly modules ile başlatılmasından sonra bazı servislerin konteynırdan çözülebileceğini doğrulamaya odaklanır. Ben kapsüllemek rağmen

+0

+1. Peter ile tamamen aynı fikirdeyim. Kapsayıcıyı test yöntemlerinde kullanmayın, ancak SUT'nizi el ile (veya alaycı bir çerçeve kullanarak) oluşturun. – Steven

+1

1 de - bir bileşenidir rahatça bir test fikstür elle oluşturulan edilemeyecek kadar pek çok bağımlılık varsa, çok sayıda sorumlulukları vardır. –

+0

Peter, Steven ve Nicholas'a, bu yaklaşımın neden gerekli olmadığının ardında yatan sebepler için teşekkürler. Bu yaklaşımın, elden çıkıp "birim" sınırlarını aşabilecek testler oluşturulmasını destekleyebileceğine katılıyorum. –

4

Sadece benim testler AutoFac bir bağımlılık için izin. Tüm TestFixtures, aşağıdaki gibi tanımlı olan Düzeltme aracından devralınır:

public class Fixture 
{ 
    private static readonly IContainer MainContainer = Ioc.Build(); 
    private readonly TestLifetime _testLifetime = new TestLifetime(MainContainer); 

    [SetUp] 
    public void SetUp() 
    { 
     _testLifetime.SetUp(); 
    } 

    [TearDown] 
    public void TearDown() 
    { 
     _testLifetime.TearDown(); 
    } 

    protected TService Resolve<TService>() 
    { 
     return _testLifetime.Resolve<TService>(); 
    } 

    protected void Override(Action<ContainerBuilder> configurationAction) 
    { 
     _testLifetime.Override(configurationAction); 
    } 
} 

public class TestLifetime 
{ 
    private readonly IContainer _mainContainer; 

    private bool _canOverride; 
    private ILifetimeScope _testScope; 

    public TestLifetime(IContainer mainContainer) 
    { 
     _mainContainer = mainContainer; 
    } 

    public void SetUp() 
    { 
     _testScope = _mainContainer.BeginLifetimeScope(); 
     _canOverride = true; 
    } 

    public void TearDown() 
    { 
     _testScope.Dispose(); 
     _testScope = null; 
    } 

    public TService Resolve<TService>() 
    { 
     _canOverride = false; 
     return _testScope.Resolve<TService>(); 
    } 

    public void Override(Action<ContainerBuilder> configurationAction) 
    { 
     _testScope.Dispose(); 

     if (!_canOverride) 
      throw new InvalidOperationException("Override can only be called once per test and must be before any calls to Resolve."); 

     _canOverride = false; 
     _testScope = _mainContainer.BeginLifetimeScope(configurationAction); 
    } 
} 
İlgili konular