2009-06-10 17 views
5

मैं व्यवहार संचालित विकास पर अपना हाथ आजमा रहा हूं और मैं अपने डिजाइन को अनुमान लगा रहा हूं क्योंकि मैं इसे लिख रहा हूं। यह मेरी पहली ग्रीनफील्ड परियोजना है और यह सिर्फ अनुभव की कमी हो सकती है। वैसे भी, मैं जिस कक्षा में लिख रहा हूं उसके लिए यहां एक सरल कल्पना है। यह समर्पित व्यवहार संचालित ढांचे का उपयोग करने के बजाय एनडीआईटी में बीडीडी शैली में लिखा गया है। ऐसा इसलिए है क्योंकि परियोजना .NET 2.0 को लक्षित करती है और सभी बीडीडी ढांचे ने .NET 3.5 को गले लगा लिया है।क्या यह एक खराब डिजाइन है?

[TestFixture] 
public class WhenUserAddsAccount 
{ 
    private DynamicMock _mockMainView; 
    private IMainView _mainView; 

    private DynamicMock _mockAccountService; 
    private IAccountService _accountService; 

    private DynamicMock _mockAccount; 
    private IAccount _account; 

    [SetUp] 
    public void Setup() 
    { 
     _mockMainView = new DynamicMock(typeof(IMainView)); 
     _mainView = (IMainView) _mockMainView.MockInstance; 

     _mockAccountService = new DynamicMock(typeof(IAccountService)); 
     _accountService = (IAccountService) _mockAccountService.MockInstance; 

     _mockAccount = new DynamicMock(typeof(IAccount)); 
     _account = (IAccount)_mockAccount.MockInstance; 
    } 

    [Test] 
    public void ShouldCreateNewAccount() 
    { 
     _mockAccountService.ExpectAndReturn("Create", _account); 
     MainPresenter mainPresenter = new MainPresenter(_mainView, _accountService); 
     mainPresenter.AddAccount(); 
     _mockAccountService.Verify(); 
    } 
} 

मेनपेंटर द्वारा उपयोग किए जाने वाले इंटरफेस में से कोई भी वास्तविक कार्यान्वयन अभी तक नहीं है। नए खाते बनाने के लिए खाता सेवा जिम्मेदार होगी। अलग-अलग प्लगइन के रूप में परिभाषित IAccount के कई कार्यान्वयन हो सकते हैं। रनटाइम पर, यदि एक से अधिक हो तो उपयोगकर्ता को यह चुनने के लिए कहा जाएगा कि कौन सा खाता प्रकार बनाना है। अन्यथा खाता सेवा केवल एक खाता बनायेगी।

उन चीजों में से एक जो मुझे असहज है, यह है कि केवल एक ही नमूना/परीक्षण लिखने के लिए कितने मोजे की आवश्यकता होती है। क्या यह बीडीडी का उपयोग करने का सिर्फ एक दुष्प्रभाव है या क्या मैं इस चीज़ के बारे में गलत तरीके से जा रहा हूं?

[अपडेट]

यहाँ MainPresenter.AddAccount

public void AddAccount() 
    { 
     IAccount account; 
     if (AccountService.AccountTypes.Count == 1) 
     { 
      account = AccountService.Create(); 
     } 
     _view.Accounts.Add(account); 
    } 

का वर्तमान क्रियान्वयन किसी भी सुझाव, सुझाव या विकल्प का स्वागत करते है।

उत्तर

3

जब विकास के लिए शीर्ष पर किया जा रहा है तो बहुत सारे मोजे का उपयोग करके खुद को ढूंढना काफी आम है। जिन टुकड़ों की आपको आवश्यकता है वे स्वाभाविक रूप से आपको नकल करने की आवश्यकता नहीं है। इसके साथ ही यह एक स्वीकृति स्तर परीक्षण की तरह महसूस करता है। मेरे अनुभव में बीडीडी या संदर्भ/विशिष्टता इकाई परीक्षण स्तर पर थोड़ा अजीब होना शुरू होता है। इकाई परीक्षण के स्तर मैं शायद की तर्ज पर अधिक कुछ कर रही होगी ...

 
when_adding_an_account 
    should_use_account_service_to_create_new_account 
    should_update_screen_with_new_account_details 

पर आप IAccount के लिए एक इंटरफेस के आपके उपयोग पर पुनर्विचार कर सकते हैं। डोमेन इकाइयों पर सेवाओं के लिए इंटरफेस रखने के साथ मैं व्यक्तिगत रूप से चिपकता हूं। लेकिन यह एक व्यक्तिगत वरीयता है।

कुछ अन्य छोटे सुझाव ...

  • आप इस तरह के राइनो Mocks (या MOQ) जो आप अपने दावे के लिए तार का उपयोग कर से बचने के लिए अनुमति देते हैं के रूप में एक मजाक ढांचे के उपयोग पर विचार कर सकते हैं।
 

    _mockAccountService.Expect(mock => mock.Create()) 
    .Return(_account); 

  • आप BDD शैली एक आम पैटर्न मैंने देखा है परीक्षण सेटअप के लिए श्रृंखलित कक्षाएं उपयोग कर रहा है कर रहे हैं। अपने उदाहरण में ...
 
public class MainPresenterSpec 
{ 
    // Protected variables for Mocks 

    [SetUp] 
    public void Setup() 
    { 
     // Setup Mocks 
    } 

} 

[TestFixture] 
public class WhenUserAddsAccount : MainPresenterSpec 
{ 
    [Test] 
    public void ShouldCreateNewAccount() 
    { 
    } 
} 
  • इसके अलावा, मैं एक गार्ड खंड का उपयोग करने के लिए अपने कोड को बदलने की सलाह देते हैं ..
 
    public void AddAccount() 
    { 
     if (AccountService.AccountTypes.Count != 1) 
     { 
      // Do whatever you want here. throw a message? 
     return; 
     } 

    IAccount account = AccountService.Create(); 

     _view.Accounts.Add(account); 
    } 
+0

यहां कुछ अच्छी सलाह +1 –

1

ऐसा लगता है कि एक प्रस्तुति के लिए एक प्रस्तुति के लिए सही संख्या की तरह लगता है जो एक खाता वापस लेना है।

यह यूनिट परीक्षण की बजाय स्वीकृति परीक्षण की तरह लगता है, यद्यपि - यदि आप अपने दावे की जटिलता को कम करते हैं तो आपको चिंताओं का एक छोटा सा सेट मिल जाएगा।

0

आप क्रम में MockContainers उपयोग करने के लिए, सभी नकली प्रबंधन से छुटकारा पाने के प्रस्तोता बनाने के दौरान चाहते हो सकता है। यह यूनिट परीक्षणों को बहुत सरल बनाता है।

0

यह ठीक है, लेकिन मैं वहाँ कहीं में एक आईओसी automocking कंटेनर उम्मीद करेंगे। कोड टेस्ट लेखक पर मैन्युअल रूप से (स्पष्ट रूप से) परीक्षणों में मैक और वास्तविक ऑब्जेक्ट्स के बीच स्विचिंग करता है, जो मामला नहीं होना चाहिए क्योंकि अगर हम इकाई परीक्षण (यूनिट के साथ केवल एक वर्ग के साथ) के बारे में बात कर रहे हैं, तो यह केवल ऑटो के लिए आसान है सभी अन्य वर्गों का मज़ाक उड़ाओ और चट्टानों का उपयोग करें। अधिक एकीकरण परीक्षण की तरह -

मैं क्या कह रहा हूँ कि यदि आप एक परीक्षण वर्ग दोनों mainView और mockMainView का उपयोग करता है है, तो आप शब्द के सही अर्थों में एक इकाई परीक्षण की जरूरत नहीं है है।

+0

यह उदाहरण में उपयोग किए जाने वाले मॉकिंग फ्रेमवर्क का एक आर्टिफैक्ट है। NUnit.Mocks अन्य नकली ढांचे के रूप में बहुमुखी नहीं है। प्रत्येक नकली स्वतंत्र रूप से बनाया, कॉन्फ़िगर और सत्यापित किया जाता है। मैंने तब से स्विच किया है जो सृजन और सत्यापन के लिए एक कंटेनर का उपयोग करता है, जिससे प्रत्येक मॉक की कॉन्फ़िगरेशन को अलग से संभाला जा सकता है। –

2

परीक्षण जीवन समर्थन बहुत आसान है यदि आप एक ऑटो मॉकिंग कंटेनर जैसे RhinoAutoMocker (StructureMap का हिस्सा) का उपयोग करते हैं। परीक्षण के तहत कक्षा बनाने के लिए आप ऑटो मॉकिंग कंटेनर का उपयोग करते हैं और परीक्षण (ओं) के लिए आवश्यक निर्भरताओं के लिए पूछते हैं। कंटेनर को कन्स्ट्रक्टर में 20 चीजें इंजेक्ट करने की आवश्यकता हो सकती है, लेकिन अगर आपको केवल एक परीक्षण करने की आवश्यकता है तो आपको केवल उस के लिए पूछना होगा।

using StructureMap.AutoMocking; 

namespace Foo.Business.UnitTests 
{ 
    public class MainPresenterTests 
    { 
     public class When_asked_to_add_an_account 
     { 
      private IAccountService _accountService; 
      private IAccount _account; 
      private MainPresenter _mainPresenter; 

      [SetUp] 
      public void BeforeEachTest() 
      { 
       var mocker = new RhinoAutoMocker<MainPresenter>(); 
       _mainPresenter = mocker.ClassUnderTest; 
       _accountService = mocker.Get<IAccountService>(); 
       _account = MockRepository.GenerateStub<IAccount>(); 
      } 

      [TearDown] 
      public void AfterEachTest() 
      { 
       _accountService.VerifyAllExpectations(); 
      } 

      [Test] 
      public void Should_use_the_AccountService_to_create_an_account() 
      { 
       _accountService.Expect(x => x.Create()).Return(_account); 
       _mainPresenter.AddAccount(); 
      } 
     } 
    } 
} 

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

Foo.Business.UnitTests.MainPresenterTest 
    When_asked_to_add_an_account 
    Should_use_the_AccountService_to_create_an_account 
    Should_add_the_Account_to_the_View 
+0

+1 अनुशंसित वृद्धि के साथ-साथ एक वृद्धि को अपनाने के लिए उपकरणों की सिफारिश करने के लिए +1। –

0

यह मेरी राय है यदि आप पाते हैं अपने आप को mocks की आवश्यकता होगी, कि, अपने डिजाइन सही नहीं है: जब NUnit में चलाने इस आप की तरह एक संदर्भ देता है।

अवयवों को स्तरित किया जाना चाहिए। आप अलगाव में घटक ए का निर्माण और परीक्षण करते हैं। फिर आप बी + ए का निर्माण और परीक्षण करते हैं। एक बार खुश होने के बाद, आप परत सी और परीक्षण सी + बी + ए का निर्माण करते हैं।

आपके मामले में आप एक "_mockAccountService" की जरूरत नहीं होनी चाहिए। यदि आपका वास्तविक खाता सेवा परीक्षण किया गया है, तो बस इसका इस्तेमाल करें। इस तरह आप जानते हैं कि किसी भी बग मुख्य प्रतिनिधि में हैं और नकली में नहीं।

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

+0

मोक्स उपयोगी होंगे यदि आप ए + बी को विभिन्न टीमों द्वारा समवर्ती रूप से विकसित करना चाहते थे (बी डेवलपर्स को नकली ए की आवश्यकता होगी)। और यदि ए घटक वास्तव में धीमा है तो आप बी को डिलीप करते समय एक नकली ए से बहुत खुश होंगे। (ऐसा कहकर, प्रैक्टिस में मैं खुद को ए कर रहा हूं, फिर ए + बी परीक्षण जो आप बहुमत के मामलों में वर्णन करते हैं)। – timday

+0

मोक्स अप्रत्यक्ष आउटपुट का परीक्षण करने के लिए एक अच्छा और आसान तरीका है जो अनदेखा करने के लिए महत्वपूर्ण और असुरक्षित हैं। –

+1

परीक्षण के तहत कक्षा को अलग करने के लिए मैक्स का उपयोग करने की क्षमता सही डिजाइन के संकेत की तरह दिखती है, गलत डिजाइन नहीं। जब मैं जेमॉक तक पहुंच सकता हूं, तो मैं अपने सिस्टम के डिज़ाइन के साथ और अधिक आरामदायक हूं, क्योंकि मैं स्तरित के साथ होता था, जो डेटाबेस नेस्टेड-लेयर दृष्टिकोण से कसकर बंधे थे। –

1

हां, आपका डिज़ाइन त्रुटिपूर्ण है। आप mocks :)

अधिक गंभीरता से, मैं पिछले पोस्टर जो, पता चलता है अपने डिजाइन स्तरित किया जाना चाहिए ताकि प्रत्येक परत अलग से परीक्षण किया जा सकता से सहमत उपयोग कर रहे हैं। मुझे लगता है कि यह सिद्धांत में गलत है कि परीक्षण कोड को वास्तविक उत्पादन कोड को बदलना चाहिए - जब तक यह स्वचालित रूप से और पारदर्शी तरीके से नहीं किया जा सकता है, जिस तरह से डीबग या रिलीज के लिए कोड को संकलित किया जा सकता है।

यह हेज़ेनबर्ग अनिश्चितता सिद्धांत की तरह है - एक बार जब आपके पास मोक्स हो जाए, तो आपका कोड इतना बदल जाता है कि यह रखरखाव सिरदर्द बन जाता है और मोक्स के पास बग्स को पेश करने या मास्क करने की क्षमता होती है।

आप साफ इंटरफेस है, तो मैं एक सरल अंतरफलक है कि simulates (या मजाक उड़ाता है) एक और मॉड्यूल के लिए एक लागू नहीं किया गया इंटरफ़ेस को लागू करने के साथ कोई झगड़ा नहीं है। यूनिट परीक्षण आदि के लिए, इस सिमुलेशन का उपयोग मॉकिंग के समान तरीके से किया जा सकता है।

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