2011-05-11 8 views
8

मैं निम्नलिखित की तरह एक विधि है:इकाई परीक्षण शून्य विधि है कि एक नई वस्तु बनाता है

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

मैं परीक्षण करना चाहते हैं कि enum मूल्य कि ICommand वस्तु मैं के निर्माता करने के लिए पारित हो जाता है मी बनाने का सही मूल्य है। क्या राइनो के साथ ऐसा कोई तरीका है। मैक्स?

+0

क्या एनम मूल्य अप्रत्याशित है तो कन्स्ट्रक्टर अपवाद फेंकता है? – MattDavey

+0

निर्माता स्वयं फेंक नहीं देता है। तर्क एक निष्पादन विधि द्वारा निष्पादन विधि द्वारा पास किए जाते हैं जो एक नेविगेशन प्रबंधक वर्ग के माध्यम से बनाया गया है। नया दृश्य मॉडल तब कुछ प्रदर्शन संपत्ति के लिए enum का उपयोग करता है और यदि यह एक अप्रत्याशित मान है, तो यह फेंकता है। – alimbada

उत्तर

11

विकल्प 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); 
} 

इस तरह से आप को रोक सकता: एक सीवन

सबसे आसान तरीका है एक सीवन के साथ कि विधि refactor करने के लिए है का उपयोग करें इस वर्ग को विस्तारित करके 'नए' ऑपरेटर का निर्माण। हाथ से ऐसा करने पर, यह ऐसा दिखाई दे सकता है:

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() { } 
    } 
} 

यह नकली कक्षा आपके परीक्षण विधियों में उपयोग की जा सकती है।


विकल्प 2: अलग व्यवहार और डेटा

एक बेहतर तरीका व्यवहार से डेटा को अलग करने होगा। आपके आदेश में डेटा (संदेश) और व्यवहार (उस संदेश को संभालने) दोनों हैं। यदि आपको अपने कोड बेस में ऐसा परिवर्तन करने की अनुमति है: इसे अलग करें, उदाहरण के लिए कमांड और कमांड हैंडलर को परिभाषित करके।

// 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 
    } 
} 

अब आप वर्ग आप परीक्षण करना चाहते में एक हैंडलर सुई निर्भरता इंजेक्शन उपयोग कर सकते हैं: यहाँ एक उदाहरण है। यह वर्ग अब इस तरह दिखेगा:

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

अब आप व्यवहार से डेटा, यह एक नकली आदेश हैंडलर बनाने के लिए बहुत आसान हो जाएगा अलग के बाद से (या राइनो का मजाक उड़ाता है का उपयोग करते हुए इसे बनाने) की जाँच करता है कि अगर सही कमांड हैंडलर को भेजा गया था। मैन्युअल रूप से यह इस तरह दिखेगा:

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

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

यह नकली हैंडलर आपके यूनिट परीक्षण प्रोजेक्ट में पुन: उपयोग किया जा सकता है। इस FakeHandler का उपयोग कर ऐसा दिखाई दे सकता एक परीक्षण:

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

व्यवहार से डेटा को अलग न केवल अपने आवेदन अधिक परीक्षण योग्य बनाता है। यह आपके एप्लिकेशन को बदलने के लिए अधिक लचीला बनाता है। उदाहरण के लिए, सिस्टम में किसी भी हैंडलर में परिवर्तन करने की आवश्यकता के बिना, आदेशों को निष्पादित करने के लिए क्रॉस-कटिंग चिंताओं को जोड़ा जा सकता है।चूंकि IHandler<T> एक विधि के साथ एक इंटरफ़ेस है, तो decorator लिखना बहुत आसान है जो प्रत्येक हैंडलर को लपेट सकता है और लॉगिंग, ऑडिट ट्रेलिंग, प्रोफाइलिंग, सत्यापन, लेनदेन हैंडलिंग, गलती सहनशीलता सुधार आदि जैसी चीजें जोड़ सकता है। आप और अधिक पढ़ सकते हैं इसके बारे में this article में।

+0

मैं आपके पहले सुझाव के साथ गया था। नकली वर्ग हालांकि आवश्यक नहीं था। मैं Rhino.Mocks 'AssertWasCalled (...) का उपयोग करने में सक्षम था सही पैरामीटर को CreateCommand (...) विधि में पारित किया गया था। धन्यवाद! :-) – alimbada

+0

@ अलिमबाडा: धन्यवाद। लेकिन हैंडलर के बारे में मत भूलना। इस बारे में सोचें कि यह आपके परीक्षणों और आपके डिज़ाइन को सरल कैसे बना सकता है। – Steven

2

कोई भी जिसे मैं जानता हूं। मेरे दिमाग में आने वाली सबसे नज़दीकी चीज एक कारखाने का उपयोग करना है, फिर उस कारखाने के StrictMock बनाएं। ऐसा ही कुछ:

readonly ICommandFactory factory; 

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

फिर, आप Create() को invokation पर उम्मीदों रख सकते हैं।

HTH

+0

यदि आप ऐसा करते हैं तो आपको शायद प्रति विशिष्ट कमांड फ़ैक्टरी की आवश्यकता होगी। ओपी के मामले में कक्षा को 'IMyCommandFactory' की आवश्यकता होती है। यह शायद कम आदर्श होगा। अभी भी (कन्स्ट्रक्टर) निर्भरता इंजेक्शन का उपयोग करने के लिए +1। – Steven

संबंधित मुद्दे