2011-05-11 10 views
8

aşağıdaki gibi bir yöntem vardır:Birim test boşluk yöntemi

public void ExecuteSomeCommand() 
{ 
    new MyCommand(someInt, SomeEnum.EnumValue).Execute(); 
} 
test etmek istiyorum

o ben iCommand nesnenin kurucusuna geçirilir sıralama değeri m oluşturma doğru değerdir. Bunu Rhino.Mocks ile yapmamın bir yolu var mı?

+0

Enum değeri beklenmiyorsa kurucu bir istisna atar mı? – MattDavey

+0

Yapıcının kendisi atmıyor. Argümanlar Execute yöntemi tarafından bir NavigationManager sınıfı aracılığıyla oluşturulan bir görünümün modeline geçirilir. Yeni görünüm modeli daha sonra bazı görüntüleme özelliklerinin enumunu kullanır ve beklenmedik bir değer ise atar. – alimbada

cevap

11

Seçenek 1:

public void ExecuteSomeCommand() 
{ 
    this.CreateCommand(someInt, SomeEnum.EnumValue).Execute(); 
} 

// Your seam 
protected virtual ICommand CreateCommand(int someInt, 
    SomeEnum someEnum) 
{ 
    return new MyCommand(someInt, SomeEnum.EnumValue); 
} 

sen kesmek Bu şekilde: Bir bir dikiş

kolay yolu bir dikişle bu yöntemi refactor olduğunu kullanın Bu sınıfı genişleterek 'yeni' operatörün oluşturulması. elle Bunu yaparken, bu gibi görünebilir:

public FakeSomeService : SomeService 
{ 
    public int SomeInt; 
    public SomeEnum SomeEnum; 

    protected override Command CreateCommand(int someInt, 
     SomeEnum someEnum) 
    { 
     this.SomeInt = someInt; 
     this.SomeEnum = someEnum; 
     return new FakeCommand(); 
    } 

    private sealed class FakeCommand : Command 
    { 
     public override void Execute() { } 
    } 
} 

Bu sahte sınıf

test yöntemlerinde kullanılabilir.


Seçenek 2: Ayrı davranışı ve veri

daha iyi bir yol davranış verileri ayrı olacaktır. Komutun hem verileri (mesajı) hem de davranışı vardır (bu iletiyi işlerken). Kod tabanınızda böyle bir değişiklik yapmanıza izin verilirse: örneğin, komutları ve komut işleyicilerini tanımlayarak bunu ayırın.

// Define an interface for handling commands 
public interface IHandler<TCommand> 
{ 
    void Handle(TCommand command); 
} 

// Define your specific command 
public class MyCommand 
{ 
    public int SomeInt; 
    public SomeEnum SomeEnum; 
} 

// Define your handler for that command 
public class MyCommandHandler : IHandler<MyCommand> 
{ 
    public void Handle(MyCommand command) 
    { 
     // here your old execute logic 
    } 
} 

Şimdi test etmek istediğiniz sınıfa bir işleyici enjekte etmek bağımlılık enjeksiyon kullanabilirsiniz: İşte bir örnek. Bu sınıf artık şu şekilde görünecektir: Şimdi davranış verileri, sahte komut işleyicisi oluşturmak çok kolay olacak ayrılmış yana

public class SomeService 
{ 
    private readonly IHandler<MyCommand> handler; 

    // Inject a handler here using constructor injection. 
    public SomeService(IHandler<MyCommand> handler) 
    { 
     this.handler = handler; 
    } 

    public void ExecuteSomeCommand() 
    { 
     this.handler.Handle(new MyCommand 
     { 
      SomeInt = someInt, 
      SomeEnum = someEnum 
     }); 
    } 
} 

(veya Rhino alay kullanarak oluşturun) kontrol ettiğinden doğru komuta eğer işleyiciye gönderildi. Bu, aşağıdaki gibi olacaktır:

public class FakeHandler<TCommand> : IHandler<TCommand> 
{ 
    public TCommand HandledCommand { get; set; } 

    public void Handle(TCommand command) 
    { 
     this.HandledCommand = command; 
    } 
} 

Bu sahte işleyici ünite test projenizde yeniden kullanılabilir. Bu gibi görünebilir bu FakeHandler kullanarak bir test:

[TestMethod] 
public void SomeTestMethod() 
{ 
    // Arrange 
    int expected = 23; 

    var handler = new FakeHandler<MyCommand>(); 

    var service = new SomeService(handler); 

    // Act 
    service.ExecuteSomeCommand(); 

    // Assert 
    Assert.AreEqual(expected, handler.HandledCommand.SomeInt); 
} 

davranış verileri ayırmak uygulama daha sınanabilir yapar sadece. Uygulamanızı değiştirmek için daha esnek hale getirir. Örneğin, sistemdeki herhangi bir işleyicide değişiklik yapmak zorunda kalmadan komutların yürütülmesine çapraz kesim endişeleri eklenebilir.IHandler<T>, tek bir yöntemle bir arabirim olduğundan, her işleyiciyi sarmalayabilen ve günlüğe kaydetme, denetim takibi, profil oluşturma, doğrulama, işlem işleme, hataya dayanıklılık geliştiricileri vb. Gibi şeyler ekleyen bir decorator yazmak çok kolaydır. Bu konuda this article.

+0

İlk önerinizle gittim. Sahte sınıf gerekli değildi. Doğru parametreleri CreateCommand (...) yöntemine iletildiğini kontrol etmek için Rhino.Mocks 'AssertWasCalled (...) kullanabildim. Teşekkürler! :-) – alimbada

+0

@Alimbada: Teşekkürler. Ama işleyicileri unutma. Bunun testlerinizi ve tasarımınızı nasıl basitleştirebileceğini düşünün. – Steven

2

Bildiğim kadarıyla. Aklıma gelen en yakın şey, bir fabrika kullanmak ve o fabrikada oluşturmaktır. Bunun gibi bir şey:

readonly ICommandFactory factory; 

public Constructor(ICommandFactory factory) 
{ 
    this.factory = factory; 
} 
public void ExecuteSomeCommand() 
{ 
    factory.Create(someInt, SomeEnum.EnumValue).Execute(); 
} 

o zaman, Create() için invokation üzerinde beklentilerini yerleştirebilirsiniz.

HTH

+0

Bunu yaparsanız, muhtemelen her komut için bir komut fabrikasına ihtiyacınız olacaktır. OP durumunda bu sınıf bir 'IMyCommandFactory' gerektirir. Bu muhtemelen daha az ideal olur. Hala (constructor) bağımlılık enjeksiyonunu kullanmak için +1. – Steven