एंटिटी फ्रेमवर्क 6 के साथ लोग यूनिट परीक्षण कैसे कर रहे हैं, क्या आपको परेशान होना चाहिए?


170

मैं अभी यूनिट टेस्टिंग और टीडीडी को सामान्य रूप से शुरू कर रहा हूं। मैंने पहले भी डब किया है, लेकिन अब मैं इसे अपने वर्कफ़्लो में जोड़ने और बेहतर सॉफ़्टवेयर लिखने के लिए दृढ़ हूं।

मैंने कल एक सवाल पूछा था जिसमें इस तरह का समावेश था, लेकिन यह अपने आप में एक सवाल है। मैं एक सेवा वर्ग को लागू करना शुरू करने के लिए बैठ गया हूं जिसका उपयोग मैं नियंत्रक से व्यापार तर्क को दूर करने के लिए और विशिष्ट मॉडल और ईएफ़ 6 का उपयोग करके डेटा इंटरैक्शन से मैप करने के लिए करूंगा।

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

यहाँ मुझे लगता है कि यह सवाल है, क्या ऐसा करने का कोई मतलब है? यदि ऐसा है, तो लोग IQueryable की वजह से लीक हुए सार के प्रकाश में जंगली में कैसे कर रहे हैं और यूनिट परीक्षण के विषय पर लादिस्लाव मिर्का द्वारा कई महान पदों को सीधे-सीधे नहीं किया जा रहा है क्योंकि लाइनक प्रदाताओं में अंतर स्मृति में काम कर रहा है। एक विशिष्ट डेटाबेस के लिए लागू के रूप में कार्यान्वयन।

मैं जिस कोड का परीक्षण करना चाहता हूं वह बहुत सरल लगता है। (यह मैं क्या कर रहा हूँ समझने की कोशिश करने और समझने के लिए सिर्फ डमी कोड है, मैं TDD का उपयोग करके निर्माण को चलाना चाहता हूं)

प्रसंग

public interface IContext
{
    IDbSet<Product> Products { get; set; }
    IDbSet<Category> Categories { get; set; }
    int SaveChanges();
}

public class DataContext : DbContext, IContext
{
    public IDbSet<Product> Products { get; set; }
    public IDbSet<Category> Categories { get; set; }

    public DataContext(string connectionString)
                : base(connectionString)
    {

    }
}

सर्विस

public class ProductService : IProductService
{
    private IContext _context;

    public ProductService(IContext dbContext)
    {
        _context = dbContext;
    }

    public IEnumerable<Product> GetAll()
    {
        var query = from p in _context.Products
                    select p;

        return query;
    }
}

वर्तमान में मैं कुछ चीजें करने की मानसिकता में हूं:

  1. इस दृष्टिकोण की तरह कुछ के साथ मॉकिंग EF प्रसंग - Mocking EF जब इकाई परीक्षण या सीधे moq जैसे इंटरफ़ेस पर एक नकली रूपरेखा का उपयोग कर - दर्द है कि इकाई परीक्षण पास हो सकता है लेकिन जरूरी नहीं कि अंत तक काम करें और उन्हें एकीकरण परीक्षणों के साथ वापस करें?
  2. हो सकता है कि कुछ इस तरह का उपयोग कर प्रयास नकली एफई के लिए - मैं इसे इस्तेमाल नहीं किया है और यह सुनिश्चित करें किसी और जंगली में यह उपयोग कर रहा है, तो क्यों नहीं?
  3. किसी भी चीज़ का परीक्षण करने की जहमत न उठाएं, जो केवल EF को वापस बुलाती है - इसलिए अनिवार्य रूप से सेवा विधियाँ जो EF को सीधे कॉल करती हैं (getAll आदि) यूनिट परीक्षण नहीं हैं, लेकिन सिर्फ एकीकरण परीक्षण हैं?

वहाँ कोई भी वास्तव में एक रेपो के बिना वहाँ बाहर कर रहा है और सफलता हो रही है?


हे मोडिका, मैं इस बारे में हाल ही में सोच रहा था (इस प्रश्न के कारण: stackoverflow.com/questions/25977388/… ) इसमें मैं थोड़ा और औपचारिक रूप से वर्णन करने की कोशिश करता हूं कि मैं इस समय कैसे काम करता हूं, लेकिन मुझे यह सुनना अच्छा लगेगा कि कैसे आप कर रहे हैं।
samy

हाय @samy, जिस तरह से हमने इसे करने का फैसला किया वह यूनिट परीक्षण कुछ भी नहीं था जो सीधे ईएफ को छूता था। क्वेरीज़ का परीक्षण किया गया था लेकिन एकीकरण परीक्षण के रूप में, यूनिट परीक्षण नहीं। मॉकिंग ईएफ थोड़ा गंदा लगता है, लेकिन यह प्रोजेक्ट छोटा था, इसलिए एक डेटाबेस को हिट करने वाले परीक्षणों के भार का प्रदर्शन प्रभाव वास्तव में चिंता का विषय नहीं था, इसलिए हम इसके बारे में थोड़ा अधिक व्यावहारिक हो सकते हैं। मुझे अभी भी 100% यकीन नहीं है कि सबसे अच्छा तरीका क्या है जो आपके साथ पूरी तरह से सच है, कुछ बिंदु पर आप ईएफ (और अपने डीबी) और इकाई परीक्षण को हिट करने जा रहे हैं, मुझे यहां सही नहीं लगता है।
मोदिका

जवाबों:


186

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

हालाँकि, आप नीचे डेटाबेस खुद करते हैं! यह वह जगह है जहां मेरी राय में यह दृष्टिकोण टूट जाता है, आपको यह परीक्षण करने की आवश्यकता नहीं है कि ईएफ / एनएच अपने काम सही तरीके से कर रहे हैं। आपको यह परीक्षण करने की आवश्यकता है कि आपके मैपिंग / कार्यान्वयन आपके डेटाबेस के साथ काम कर रहे हैं। मेरी राय में यह एक प्रणाली के सबसे महत्वपूर्ण भागों में से एक है जिसे आप परीक्षण कर सकते हैं।

कड़ाई से बोलते हुए भी हम इकाई परीक्षण के क्षेत्र से बाहर जा रहे हैं और एकीकरण परीक्षण में हैं लेकिन सिद्धांत समान हैं।

पहली चीज जो आपको करने की ज़रूरत है वह आपके डीएएल को मॉक करने में सक्षम है ताकि आपके बीएलएल को ईएफ और एसक्यूएल के स्वतंत्र रूप से परीक्षण किया जा सके। ये आपके यूनिट टेस्ट हैं। आगे आपको अपने DAL को साबित करने के लिए अपने इंटीग्रेशन टेस्ट को डिजाइन करना होगा, मेरी राय में ये हर तरह से महत्वपूर्ण हैं।

कुछ बातों पर विचार करना होगा:

  1. आपके डेटाबेस को प्रत्येक परीक्षण के साथ एक ज्ञात स्थिति में होना चाहिए। अधिकांश सिस्टम इसके लिए या तो बैकअप का उपयोग करते हैं या स्क्रिप्ट बनाते हैं।
  2. प्रत्येक परीक्षण को दोहराने योग्य होना चाहिए
  3. प्रत्येक परीक्षण परमाणु होना चाहिए

अपने डेटाबेस को स्थापित करने के लिए दो मुख्य दृष्टिकोण हैं, पहला है यूनिटटेस्ट को डीबी स्क्रिप्ट बनाना। यह सुनिश्चित करता है कि आपका यूनिट परीक्षण डेटाबेस हमेशा प्रत्येक परीक्षण की शुरुआत में एक ही स्थिति में होगा (आप इसे सुनिश्चित करने के लिए लेनदेन में प्रत्येक परीक्षण को रीसेट कर सकते हैं या चला सकते हैं)।

आपका दूसरा विकल्प यह है कि मैं क्या करूं, प्रत्येक व्यक्तिगत परीक्षण के लिए विशिष्ट सेटअप चलाएं। मेरा मानना ​​है कि यह दो मुख्य कारणों के लिए सबसे अच्छा तरीका है:

  • आपका डेटाबेस सरल है, आपको प्रत्येक परीक्षण के लिए संपूर्ण स्कीमा की आवश्यकता नहीं है
  • प्रत्येक परीक्षण सुरक्षित है, यदि आप अपनी बनाएँ स्क्रिप्ट में एक मान बदलते हैं तो यह दर्जनों अन्य परीक्षणों को अमान्य नहीं करता है।

दुर्भाग्य से यहां आपका समझौता गति है। इन सभी परीक्षणों को चलाने के लिए, इन सभी सेटअप को चलाने के लिए / स्क्रिप्ट्स को फाड़ने में समय लगता है।

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

यह आदर्श समाधान से बहुत दूर है लेकिन व्यवहार में मुझे लगता है कि यह प्रबंधन करने के लिए बहुत आसान है (खासकर जब आपके पास कई हजार परीक्षण हैं), अन्यथा आप बड़ी संख्या में स्क्रिप्ट बना रहे हैं। शुद्धता पर व्यावहारिकता।

मुझे कोई संदेह नहीं है कि इस उत्तर को कुछ वर्षों (महीनों / दिनों) में देखूंगा और अपने आप से असहमत हूं क्योंकि मेरे दृष्टिकोण बदल गए हैं - हालांकि यह मेरा वर्तमान दृष्टिकोण है।

कोशिश करने और सब कुछ जो मैंने ऊपर कहा है, यह मेरी विशिष्ट डीबी एकीकरण परीक्षा है:

[Test]
public void LoadUser()
{
  this.RunTest(session => // the NH/EF session to attach the objects to
  {
    var user = new UserAccount("Mr", "Joe", "Bloggs");
    session.Save(user);
    return user.UserID;
  }, id => // the ID of the entity we need to load
  {
     var user = LoadMyUser(id); // load the entity
     Assert.AreEqual("Mr", user.Title); // test your properties
     Assert.AreEqual("Joe", user.Firstname);
     Assert.AreEqual("Bloggs", user.Lastname);
  }
}

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

संपादित करें 13/10/2014

मैंने कहा था कि मैं आगामी महीनों में इस मॉडल को संशोधित करूंगा। जबकि मैं काफी हद तक उस दृष्टिकोण से खड़ा हूं जिसकी मैंने वकालत की है मैंने अपने परीक्षण तंत्र को थोड़ा अद्यतन किया है। अब मैं TestSetup और TestTearDown में इकाइयाँ बनाना चाहता हूँ।

[SetUp]
public void Setup()
{
  this.SetupTest(session => // the NH/EF session to attach the objects to
  {
    var user = new UserAccount("Mr", "Joe", "Bloggs");
    session.Save(user);
    this.UserID =  user.UserID;
  });
}

[TearDown]
public void TearDown()
{
   this.TearDownDatabase();
}

फिर प्रत्येक संपत्ति का व्यक्तिगत रूप से परीक्षण करें

[Test]
public void TestTitle()
{
     var user = LoadMyUser(this.UserID); // load the entity
     Assert.AreEqual("Mr", user.Title);
}

[Test]
public void TestFirstname()
{
     var user = LoadMyUser(this.UserID);
     Assert.AreEqual("Joe", user.Firstname);
}

[Test]
public void TestLastname()
{
     var user = LoadMyUser(this.UserID);
     Assert.AreEqual("Bloggs", user.Lastname);
}

इस दृष्टिकोण के कई कारण हैं:

  • अतिरिक्त डेटाबेस कॉल नहीं हैं (एक सेटअप, एक फाड़)
  • परीक्षण अधिक दानेदार होते हैं, प्रत्येक परीक्षण एक संपत्ति की पुष्टि करता है
  • सेटअप / TearDown तर्क को टेस्ट विधियों से स्वयं हटा दिया जाता है

मुझे लगता है कि यह परीक्षण वर्ग को सरल बनाता है और परीक्षण अधिक दानेदार ( एकल आवेषण अच्छे हैं )

5/3/2015 संपादित करें

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

इस मैं के साथ मदद करने के लिए अब दो आधार वर्ग हो जाते हैं SetupPerTestऔर SingleSetup। ये दोनों वर्ग आवश्यकतानुसार रूपरेखा को उजागर करते हैं।

में SingleSetupहम एक बहुत ही एक तंत्र मेरी पहली संपादित में वर्णित के रूप है। एक उदाहरण होगा

public TestProperties : SingleSetup
{
  public int UserID {get;set;}

  public override DoSetup(ISession session)
  {
    var user = new User("Joe", "Bloggs");
    session.Save(user);
    this.UserID = user.UserID;
  }

  [Test]
  public void TestLastname()
  {
     var user = LoadMyUser(this.UserID); // load the entity
     Assert.AreEqual("Bloggs", user.Lastname);
  }

  [Test]
  public void TestFirstname()
  {
       var user = LoadMyUser(this.UserID);
       Assert.AreEqual("Joe", user.Firstname);
  }
}

हालाँकि, जो यह सुनिश्चित करते हैं कि केवल सही प्रविष्टि भरी हुई है, एक SetupPerTest दृष्टिकोण का उपयोग कर सकते हैं

public TestProperties : SetupPerTest
{
   [Test]
   public void EnsureCorrectReferenceIsLoaded()
   {
      int friendID = 0;
      this.RunTest(session =>
      {
         var user = CreateUserWithFriend();
         session.Save(user);
         friendID = user.Friends.Single().FriendID;
      } () =>
      {
         var user = GetUser();
         Assert.AreEqual(friendID, user.Friends.Single().FriendID);
      });
   }
   [Test]
   public void EnsureOnlyCorrectFriendsAreLoaded()
   {
      int userID = 0;
      this.RunTest(session =>
      {
         var user = CreateUserWithFriends(2);
         var user2 = CreateUserWithFriends(5);
         session.Save(user);
         session.Save(user2);
         userID = user.UserID;
      } () =>
      {
         var user = GetUser(userID);
         Assert.AreEqual(2, user.Friends.Count());
      });
   }
}

सारांश में, दोनों दृष्टिकोण उस परीक्षण के आधार पर काम करते हैं जो आप परीक्षण करने की कोशिश कर रहे हैं।


2
एकीकरण परीक्षण के लिए यहां एक अलग दृष्टिकोण है। टीएल; डीआर - परीक्षण डेटा को सेटअप करने के लिए स्वयं एप्लिकेशन का उपयोग करें, प्रति परीक्षण लेनदेन रोलबैक करें।
गर्ट अर्नाल्ड

3
@ ललित, शानदार प्रतिक्रिया। आपने EF के परीक्षण के बारे में मेरे संदेह की पुष्टि की है। मेरा प्रश्न यह है; आपका उदाहरण एक बहुत ही ठोस मामले के लिए है, जो ठीक है। हालाँकि, जैसा कि आपने उल्लेख किया है कि आपको सैकड़ों संस्थाओं का परीक्षण करने की आवश्यकता हो सकती है। हर बार एक ही मूल कोड पैटर्न को दोहराए बिना DRY सिद्धांत (अपने आप को दोहराएं नहीं) को ध्यान में रखते हुए, आप अपने समाधान को कैसे मापते हैं?
जेफरी ए। गोचिन

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

7
इस बारे में कोई भी बाड़ पर नहीं है कि आपको इकाई परीक्षण इकाई फ़्रेमवर्क को खुद से करना चाहिए या नहीं। क्या होता है कि आपको कुछ विधि का परीक्षण करने की आवश्यकता होती है जो कुछ सामान करता है और डेटाबेस में EF कॉल करने के लिए भी होता है। लक्ष्य ईएफ का मजाक उड़ाना है ताकि आप अपने बिल्ड सर्वर पर डेटाबेस की आवश्यकता के बिना इस पद्धति का परीक्षण कर सकें।
मफिन मैन

4
मुझे वास्तव में यात्रा पसंद है। समय के साथ संपादन जोड़ने के लिए धन्यवाद - यह स्रोत नियंत्रण पढ़ने और समझने की तरह है कि आपकी सोच कैसे विकसित हुई है। मैं वास्तव में कार्यात्मक (ईएफ के साथ) और इकाई (नकली ईएफ) भेद की सराहना करता हूं।
टॉम लेय्स

21

यहां एक्सपीरियंस एक्सपीरियंस फीडबैक

बहुत सारे पढ़ने के बाद मैं अपने परीक्षणों में एफर्ट का उपयोग कर रहा हूं: परीक्षणों के दौरान एक कारखाने द्वारा बनाया गया है जो मेमोरी संस्करण में एक रिटर्न देता है, जो मुझे हर बार एक खाली स्लेट के खिलाफ परीक्षण करने देता है। परीक्षणों के बाहर, कारखाने को एक के लिए हल किया जाता है जो पूरे संदर्भ देता है।

हालाँकि, मुझे लगता है कि डेटाबेस के एक पूर्ण रुप से नकली परीक्षण के खिलाफ परीक्षण नीचे खींचने के लिए जाता है; आपको एहसास है कि आपको सिस्टम के एक हिस्से का परीक्षण करने के लिए निर्भरता का एक पूरा गुच्छा स्थापित करने का ध्यान रखना होगा। आप एक साथ उन परीक्षणों के आयोजन की ओर भी प्रवृत्त होते हैं जो संबंधित नहीं हो सकते हैं, सिर्फ इसलिए कि केवल एक बहुत बड़ी वस्तु है जो सब कुछ संभालती है। यदि आप ध्यान नहीं देते हैं, तो आप इकाई परीक्षण के बजाय एकीकरण परीक्षण कर सकते हैं

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

इसलिए मुझे एफर्ट दिलचस्प लगता है; यदि आपको जमीन पर दौड़ने की जरूरत है तो यह जल्दी से शुरू करने और परिणाम प्राप्त करने के लिए एक अच्छा उपकरण है। हालाँकि मुझे लगता है कि कुछ अधिक सुरुचिपूर्ण और सार अगला कदम होना चाहिए और यही मैं आगे की जांच करने जा रहा हूं। यह देखने के लिए कि यह आगे कहाँ जाता है :)

जोड़ने के लिए संपादित करें : प्रयास को गर्म होने में कुछ समय लगता है, इसलिए आप लगभग देख रहे हैं। परीक्षण शुरू होने पर 5 सेकंड। यह आपके लिए एक समस्या हो सकती है यदि आपको अपने परीक्षण सूट की बहुत कुशल होने की आवश्यकता है।


स्पष्टीकरण के लिए संपादित:

मैंने एक webservice ऐप का परीक्षण करने के लिए एफर्ट का उपयोग किया। प्रत्येक संदेश एम जो प्रवेश करता है वह IHandlerOf<M>विंडसर के माध्यम से रूट किया जाता है । Castle.Windsor IHandlerOf<M>जो घटक की निर्भरता resovles हल करता है । इन निर्भरताओं में से एक है DataContextFactory, जो हैंडलर को कारखाने के लिए पूछने की अनुमति देता है

अपने परीक्षणों में मैं सीधे IHandlerOf घटक को इंस्टेंट करता हूं, SUT के सभी उप-घटकों का मज़ाक उड़ाता है और हैंडलर को लिपटा हुआ एफर्ट-हैंडल को संभालता है DataContextFactory

इसका मतलब यह है कि मैं एक सख्त अर्थ में इकाई परीक्षण नहीं करता हूं, क्योंकि डीबी मेरे परीक्षणों से प्रभावित होता है। हालाँकि जैसा कि मैंने ऊपर कहा था कि यह मुझे दौड़ता हुआ मैदान मारता है और मैं जल्दी से आवेदन में कुछ बिंदुओं का परीक्षण कर सकता हूं


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

2
केवल अगर
एफर्ट

और प्रयास में csv लोडर के साथ स्ट्रिंग्स के लिए एक बग है, जब हम स्ट्रिंग्स में नल के बजाय '' का उपयोग करते हैं।
सैम

13

यदि आप इकाई परीक्षण कोड चाहते हैं तो आपको अपने कोड को अलग-अलग करने की आवश्यकता है (इस मामले में आपकी सेवा) बाहरी संसाधनों (जैसे डेटाबेस) से। आप शायद कुछ प्रकार के इन-मेमोरी ईएफ प्रदाता के साथ ऐसा कर सकते हैं , हालांकि बहुत अधिक सामान्य तरीका कुछ प्रकार के भंडार पैटर्न के साथ अपने ईएफ कार्यान्वयन को दूर करना है। इस अलगाव के बिना आप जो भी परीक्षण लिखते हैं वह एकीकरण परीक्षण होगा, इकाई परीक्षण नहीं।

EF कोड के परीक्षण के लिए - मैं अपनी रिपॉजिटरी के लिए स्वचालित एकीकरण परीक्षण लिखता हूं जो डेटाबेस के विभिन्न पंक्तियों को उनके आरंभीकरण के दौरान लिखते हैं, और फिर मेरे रिपॉजिटरी कार्यान्वयन को यह सुनिश्चित करने के लिए कहते हैं कि वे अपेक्षा के अनुरूप व्यवहार करते हैं (उदाहरण के लिए सुनिश्चित करें कि परिणाम सही तरीके से फ़िल्टर किए गए हैं, या) वे सही क्रम में हल कर रहे हैं)।

ये एकीकरण परीक्षण नहीं इकाई परीक्षण हैं, क्योंकि परीक्षण डेटाबेस कनेक्शन मौजूद होने पर भरोसा करते हैं, और यह कि लक्ष्य डेटाबेस में पहले से ही नवीनतम अप-टू-डेट स्कीमा स्थापित है।


धन्यवाद @ अन्याय, मैं रिपॉजिटरी पैटर्न के बारे में जानता हूं, लेकिन ayende.com/blog/4784/… जैसी चीजों को पढ़ना और अन्य चीजों के बीच खोना । 'यह अमूर्त परत नहीं चाहते हैं, लेकिन फिर ये एक क्वेरी दृष्टिकोण के बारे में और अधिक बात करते हैं जो बहुत भ्रमित करता है।
मोदिका

7
@Modika Ayende ने समालोचना के लिए रिपॉजिटरी पैटर्न का एक खराब कार्यान्वयन चुना है, और परिणामस्वरूप 100% सही है - इसका इंजीनियर पर और कोई लाभ नहीं देता है। एक अच्छा कार्यान्वयन आपके कोड की इकाई-परीक्षण योग्य भागों को DAL कार्यान्वयन से अलग करता है। NHibernate और EF का उपयोग करना सीधे इकाई परीक्षण के लिए कोड को मुश्किल (यदि असंभव नहीं है) बनाता है और एक कठोर अखंड कोडेज़ की ओर जाता है। मैं अभी भी रिपॉजिटरी पैटर्न के बारे में कुछ उलझन में हूं, हालांकि मैं 100% आश्वस्त हूं कि आपको अपने डीएएल कार्यान्वयन को किसी भी तरह से अलग करने की आवश्यकता है और रिपॉजिटरी मुझे अब तक मिली सबसे अच्छी चीज है।
जस्टिन

2
@Modika दूसरा लेख फिर से पढ़ें। "मैं नहीं चाहता कि यह अमूर्त परत है" जो वह कहते हैं वह नहीं है। साथ ही, फाउलर ( martinfowler.com/eaaatalog/repository.html ) या DDD ( dddcommunity.org/resources/ddd_terms ) से मूल रिपोजिटरी पैटर्न के बारे में पढ़ें । मूल अवधारणा को पूरी तरह से समझने के बिना naysayers पर विश्वास न करें। वे वास्तव में आलोचना करते हैं पैटर्न का एक हालिया दुरुपयोग है, न कि पैटर्न ही (हालांकि वे शायद यह नहीं जानते हैं)।
गुरिल्ला

1
@ guillaume31 मैं रिपॉजिटरी पैटर्न के खिलाफ नहीं हूं (मुझे यह समझ में नहीं आता है) मैं बस यह पता लगाने की कोशिश कर रहा हूं कि क्या मुझे इसे अमूर्त करने की जरूरत है जो उस स्तर पर पहले से ही एक अमूर्त है, और अगर मैं इसे छोड़ सकता हूं और नकल करके सीधे ईको के खिलाफ परीक्षण कर सकता हूं और मेरे परीक्षण में मेरे स्तर पर उच्चतर परीक्षण में इसका उपयोग करें। इसके अतिरिक्त, अगर मैं रेपो का उपयोग नहीं करता हूं तो मुझे ईएफ विस्तारित सुविधा सेट का लाभ मिलता है, रेपो के साथ मुझे वह नहीं मिल सकता है।
मोडिका

एक बार जब मैंने डीएएल को एक रिपॉजिटरी से अलग कर दिया, तो मुझे डेटाबेस (ईएफ) "मॉक" के लिए किसी और की आवश्यकता है। अब तक संदर्भ और विभिन्न async एक्सटेंशन (ToListAsync (), FirstOrDefaultAsync (), आदि) का मजाक उड़ाने से मेरे लिए निराशा हुई है।
केविन बर्टन

9

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

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

उस के साथ, और इस तथ्य को स्वीकार करते हुए कि ईएफ एक कार्यान्वयन है, मैं संभवतः एक रिपॉजिटरी को सार करने के विचार का समर्थन करूंगा। थोड़ा बेमानी लग रहा है? यह नहीं है, क्योंकि आप एक समस्या को हल कर रहे हैं जो आपके कोड को डेटा कार्यान्वयन से अलग कर रही है।

DDD में, रिपॉजिटरी केवल एग्रो रूट को लौटाते हैं, न कि DAO। इस तरह, रिपॉजिटरी के उपभोक्ता को डेटा कार्यान्वयन के बारे में कभी नहीं जानना चाहिए (जैसा कि इसे नहीं होना चाहिए) और हम इसका उपयोग इस समस्या को हल करने के तरीके के रूप में कर सकते हैं। इस मामले में, ईएफ द्वारा उत्पन्न वस्तु एक डीएओ है और इस तरह, आपके आवेदन से छिपा होना चाहिए। आपके द्वारा परिभाषित भंडार का यह एक और लाभ है। आप किसी व्यावसायिक वस्तु को EF ऑब्जेक्ट के बजाय उसके रिटर्न प्रकार के रूप में परिभाषित कर सकते हैं। अब रेपो क्या करता है, कॉल को EF से छिपाता है और EF की प्रतिक्रिया को मैप करता है जो कि रिपोज सिग्नेचर में परिभाषित व्यावसायिक वस्तु है। अब आप उस DbContext निर्भरता के स्थान पर उस रेपो का उपयोग कर सकते हैं जिसे आप अपनी कक्षाओं में इंजेक्ट करते हैं और फलस्वरूप, अब आप उस इंटरफ़ेस का मज़ाक उड़ा सकते हैं जिससे आपको अलग-अलग कोड में अपने कोड का परीक्षण करने के लिए नियंत्रण की आवश्यकता होती है।

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

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


8

मैं यूनिट टेस्ट कोड नहीं बनाऊंगा जो मेरे पास नहीं है। आप यहाँ क्या परीक्षण कर रहे हैं, कि MSFT कंपाइलर काम करता है?

इस कोड को परीक्षण योग्य बनाने के लिए, आपने अपने डेटा एक्सेस लेयर को अपने व्यावसायिक तर्क कोड से अलग करने के लिए लगभग कहा है। मैं जो कुछ भी करता हूं, वह अपने ईएफ सामान को ले जाता हूं और इसे (या एकाधिक) डीएओ या डीएएल वर्ग में डाल देता हूं, जिसमें एक संबंधित इंटरफ़ेस भी होता है। फिर मैं अपनी सेवा लिखता हूं, जिसमें इंटरफ़ेस के रूप में संदर्भित निर्भरता (कंस्ट्रक्टर इंजेक्शन अधिमानतः) में डीएओ या डीएएल ऑब्जेक्ट इंजेक्ट किया जाएगा। अब जिस भाग का परीक्षण किया जाना है (आपके कोड) को आसानी से DAO इंटरफ़ेस को मॉक करके और अपनी इकाई परीक्षा के अंदर आपके सेवा उदाहरण में इंजेक्ट करके परीक्षण किया जा सकता है।

//this is testable just inject a mock of IProductDAO during unit testing
public class ProductService : IProductService
{
    private IProductDAO _productDAO;

    public ProductService(IProductDAO productDAO)
    {
        _productDAO = productDAO;
    }

    public List<Product> GetAllProducts()
    {
        return _productDAO.GetAll();
    }

    ...
}

मैं लाइव डेटा एक्सेस लेयर्स को इंटीग्रेशन टेस्टिंग का हिस्सा मानूंगा, न कि यूनिट टेस्टिंग को। मैंने देखा है कि लोग डेटाबेस हाइबरनेट से पहले कितनी यात्राएं करते हैं, इस पर सत्यापन चलाते हैं, लेकिन वे एक ऐसी परियोजना पर थे जिसमें उनके डेटास्टोर में अरबों रिकॉर्ड शामिल थे और उन अतिरिक्त यात्राओं ने वास्तव में मायने रखा।


1
उत्तर के लिए धन्यवाद, लेकिन इस बात का क्या फर्क होगा कि एक रिपॉजिटरी कहा जाए जहां आप इस स्तर पर ईएफ के आंतरिक हिस्से को इसके पीछे छिपा रहे हैं? मैं वास्तव में EF को अमूर्त नहीं करना चाहता, हालाँकि मैं अभी भी IContext इंटरफ़ेस के साथ ऐसा कर सकता हूँ? मैं इसके लिए नया हूँ, कोमल
बनो

3
@ मंदिका ए रेपो भी ठीक है। आपको जो भी पैटर्न चाहिए। "मैं वास्तव में ईएफ को सार नहीं करना चाहता हूं" क्या आप परीक्षण योग्य कोड चाहते हैं या नहीं?
जोनाथन हेंसन

1
अगर आप अपनी चिंताओं को अलग नहीं करते हैं तो @Modika मेरा कहना है कि आपके पास कोई भी परीक्षण योग्य कोड नहीं होगा। डेटा एक्सेस और बिजनेस लॉजिक अच्छे रख-रखाव परीक्षणों को खींचने के लिए अलग-अलग परतों में होना चाहिए।
जोनाथन हेंसन

2
मैं सिर्फ यह नहीं महसूस करता था कि ईएफएस को रिपॉजिटरी एब्स्ट्रैक्शन में लपेटना अनिवार्य है क्योंकि आईडीबीसेट्स रेपो और संदर्भ यूओओ हैं, मैं अपने सवाल को थोड़ा अपडेट करूंगा क्योंकि यह भ्रामक हो सकता है। मुद्दा किसी भी अमूर्तता के साथ आता है और मुख्य बिंदु यह है कि वास्तव में मैं क्या परीक्षण कर रहा हूं क्योंकि मेरी कतारें एक ही सीमा में नहीं चलेंगी (linq-to-इकाइयों बनाम linq-to-Objects) तो अगर मैं सिर्फ यह परीक्षण कर रहा हूं कि मेरा परीक्षण क्या है? कॉल जो थोड़ा बेकार लगता है या मैं यहाँ अच्छी तरह से बंद हूँ?
मोडिका

1
, जबकि मैं आपके सामान्य बिंदुओं से सहमत हूं, DbContext काम की एक इकाई है और IDbSets निश्चित रूप से रिपॉजिटरी कार्यान्वयन के लिए कुछ हैं, और ऐसा सोचने वाला मैं अकेला नहीं हूं। मैं EF का मज़ाक उड़ा सकता हूं, और कुछ परत पर मुझे एकीकरण परीक्षण चलाने की आवश्यकता होगी, क्या यह वास्तव में मायने रखता है अगर मैं इसे रिपॉजिटरी में करता हूं या किसी सेवा में आगे हूं? एक DB को कसकर युग्मित किया जाना वास्तव में एक चिंता का विषय नहीं है, मुझे यकीन है कि ऐसा होता है, लेकिन मैं ऐसी किसी चीज़ की योजना नहीं बनाने जा रहा हूं जो हो ही नहीं सकती है।
मोदिका

8

मैंने इन विचारों तक पहुँचने के लिए कुछ समय के लिए ठोकर खाई है:

1- अगर मेरा आवेदन डेटाबेस तक पहुंच है, तो परीक्षण क्यों नहीं होना चाहिए? अगर डेटा एक्सेस में कुछ गड़बड़ है तो क्या होगा? परीक्षणों को पहले से जानना चाहिए और समस्या के बारे में खुद को सचेत करना चाहिए।

2- रिपोजिटरी पैटर्न कुछ कठिन और समय लेने वाला है।

इसलिए मैं इस दृष्टिकोण के साथ आया, कि मुझे नहीं लगता कि यह सबसे अच्छा है, लेकिन मेरी उम्मीदों को पूरा किया:

Use TransactionScope in the tests methods to avoid changes in the database.

यह करने के लिए यह आवश्यक है:

1- टेस्ट प्रोजेक्ट में EntityFramework स्थापित करें। 2- टेस्ट स्ट्रिंग को ऐप प्रोजेक्ट की कनेक्शन स्ट्रिंग में रखें। 3- Dll System.Transactions का टेस्ट प्रोजेक्ट में संदर्भ।

अद्वितीय साइड इफेक्ट यह है कि पहचान का बीज तब बढ़ेगा जब डालने का प्रयास किया जाएगा, तब भी जब लेनदेन समाप्त हो गया हो। लेकिन चूंकि परीक्षण एक विकास डेटाबेस के खिलाफ किए जाते हैं, इसलिए यह कोई समस्या नहीं होनी चाहिए।

नमूना कोड:

[TestClass]
public class NameValueTest
{
    [TestMethod]
    public void Edit()
    {
        NameValueController controller = new NameValueController();

        using(var ts = new TransactionScope()) {
            Assert.IsNotNull(controller.Edit(new Models.NameValue()
            {
                NameValueId = 1,
                name1 = "1",
                name2 = "2",
                name3 = "3",
                name4 = "4"
            }));

            //no complete, automatically abort
            //ts.Complete();
        }
    }

    [TestMethod]
    public void Create()
    {
        NameValueController controller = new NameValueController();

        using (var ts = new TransactionScope())
        {
            Assert.IsNotNull(controller.Create(new Models.NameValue()
            {
                name1 = "1",
                name2 = "2",
                name3 = "3",
                name4 = "4"
            }));

            //no complete, automatically abort
            //ts.Complete();
        }
    }
}

1
वास्तव में, मुझे यह समाधान बहुत पसंद है। लागू करने के लिए सरल और अधिक यथार्थवादी परीक्षण परिदृश्य। धन्यवाद!
स्लोपापा

1
EF 6 के साथ, आप DbContext.Database.BeginTransaction का उपयोग करेंगे, क्या आप नहीं करेंगे?
स्विसकरोड

5

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


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

3

मैं टिप्पणी के बारे में और संक्षिप्त चर्चा के लिए एक दृष्टिकोण साझा करना चाहता हूं लेकिन एक वास्तविक उदाहरण दिखाता हूं जिसका उपयोग मैं वर्तमान में ईएफ-आधारित सेवाओं की इकाई परीक्षण में मदद करने के लिए कर रहा हूं ।

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

यहां वे लक्ष्य हैं जो एक पैटर्न के साथ आते हैं:

  • टीम के अन्य डेवलपर्स को समझना आसान होना चाहिए
  • यह ईएफ़ कोड को सबसे संभावित स्तर पर अलग करना चाहिए
  • इसमें अजीब बहु-जिम्मेदारी इंटरफेस (जैसे "सामान्य" या "विशिष्ट" रिपॉजिटरी पैटर्न) बनाना शामिल नहीं होना चाहिए
  • यूनिट टेस्ट में इसे कॉन्फ़िगर करना और सेटअप करना आसान होना चाहिए

मैं पिछले बयानों से सहमत हूं कि ईएफ अभी भी एक कार्यान्वयन विवरण है और यह महसूस करना ठीक है कि आपको "शुद्ध" इकाई परीक्षण करने के लिए इसे अमूर्त करने की आवश्यकता है। मैं यह भी मानता हूं कि आदर्श रूप से, मैं यह सुनिश्चित करना चाहूंगा कि ईएफ कोड स्वयं काम करता है - लेकिन इसमें सैंडबॉक्स डेटाबेस, इन-मेमोरी प्रदाता, आदि शामिल हैं। मेरा दृष्टिकोण दोनों समस्याओं को हल करता है - आप सुरक्षित रूप से ईएफ-निर्भर कोड का परीक्षण कर सकते हैं और बना सकते हैं एकीकरण परीक्षण विशेष रूप से आपके ईएफ कोड का परीक्षण करने के लिए।

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

यह वह जगह है जहाँ एक उपयोगी, सरल पुस्तकालय आता है: मेडियाट्र । यह सरल-इन-प्रक्रिया संदेश भेजने की अनुमति देता है और यह कोड लागू करने वाले संचालकों के "अनुरोधों" को डिकॉउलिंग करके करता है। यह "कैसे" से "क्या" को डिकूप करने का एक अतिरिक्त लाभ है। उदाहरण के लिए, ईएफ कोड को छोटे-छोटे खंडों में संलग्न करके यह आपको अन्य प्रदाता या पूरी तरह से अलग तंत्र के साथ कार्यान्वयन को बदलने की अनुमति देता है, क्योंकि आप जो कुछ भी कर रहे हैं वह एक कार्रवाई करने का अनुरोध भेज रहा है।

निर्भरता इंजेक्शन का उपयोग (एक फ्रेमवर्क के साथ या बिना - आपकी वरीयता), हम आसानी से मध्यस्थ की नकल कर सकते हैं और यूनिट परीक्षण ईएफ कोड को सक्षम करने के लिए अनुरोध / प्रतिक्रिया तंत्र को नियंत्रित कर सकते हैं।

पहले, मान लें कि हमारे पास एक ऐसी सेवा है जिसके पास व्यावसायिक तर्क है जिसे हमें परीक्षण करने की आवश्यकता है:

public class FeatureService {

  private readonly IMediator _mediator;

  public FeatureService(IMediator mediator) {
    _mediator = mediator;
  }

  public async Task ComplexBusinessLogic() {
    // retrieve relevant objects

    var results = await _mediator.Send(new GetRelevantDbObjectsQuery());
    // normally, this would have looked like...
    // var results = _myDbContext.DbObjects.Where(x => foo).ToList();

    // perform business logic
    // ...    
  }
}

क्या आप इस दृष्टिकोण का लाभ देखना शुरू करते हैं? न केवल आप स्पष्ट रूप से सभी ईएफ से संबंधित कोड को वर्णनात्मक वर्गों में संलग्न कर रहे हैं, आप "कैसे" इस अनुरोध को लागू करने की चिंता को हटाकर अस्थिरता की अनुमति दे रहे हैं - यह वर्ग परवाह नहीं करता है कि क्या संबंधित ऑब्जेक्ट ईएफ, मोंटेगीडीबी से आते हैं या एक पाठ फ़ाइल।

अब MediatR के माध्यम से अनुरोध और हैंडलर के लिए:

public class GetRelevantDbObjectsQuery : IRequest<DbObject[]> {
  // no input needed for this particular request,
  // but you would simply add plain properties here if needed
}

public class GetRelevantDbObjectsEFQueryHandler : IRequestHandler<GetRelevantDbObjectsQuery, DbObject[]> {
  private readonly IDbContext _db;

  public GetRelevantDbObjectsEFQueryHandler(IDbContext db) {
    _db = db;
  }

  public DbObject[] Handle(GetRelevantDbObjectsQuery message) {
    return _db.DbObjects.Where(foo => bar).ToList();
  }
}

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

तो हमारी सुविधा सेवा की इकाई परीक्षण कैसा दिखता है? यह सरल है। इस मामले में, मैं मॉकिंग करने के लिए Moq का उपयोग कर रहा हूं (जो भी आपको खुश करता है उसका उपयोग करें):

[TestClass]
public class FeatureServiceTests {

  // mock of Mediator to handle request/responses
  private Mock<IMediator> _mediator;

  // subject under test
  private FeatureService _sut;

  [TestInitialize]
  public void Setup() {

    // set up Mediator mock
    _mediator = new Mock<IMediator>(MockBehavior.Strict);

    // inject mock as dependency
    _sut = new FeatureService(_mediator.Object);
  }

  [TestCleanup]
  public void Teardown() {

    // ensure we have called or expected all calls to Mediator
    _mediator.VerifyAll();
  }

  [TestMethod]
  public void ComplexBusinessLogic_Does_What_I_Expect() {
    var dbObjects = new List<DbObject>() {
      // set up any test objects
      new DbObject() { }
    };

    // arrange

    // setup Mediator to return our fake objects when it receives a message to perform our query
    // in practice, I find it better to create an extension method that encapsulates this setup here
    _mediator.Setup(x => x.Send(It.IsAny<GetRelevantDbObjectsQuery>(), default(CancellationToken)).ReturnsAsync(dbObjects.ToArray()).Callback(
    (GetRelevantDbObjectsQuery message, CancellationToken token) => {
       // using Moq Callback functionality, you can make assertions
       // on expected request being passed in
       Assert.IsNotNull(message);
    });

    // act
    _sut.ComplexBusinessLogic();

    // assertions
  }

}

आप देख सकते हैं कि हम सभी की जरूरत है एक एकल सेटअप है और हमें अतिरिक्त कुछ भी कॉन्फ़िगर करने की आवश्यकता नहीं है - यह एक बहुत ही सरल इकाई परीक्षण है। आइए स्पष्ट हों: यह पूरी तरह से संभव है बिना मेडिट्र के कुछ के बिना (आप बस एक इंटरफ़ेस लागू करेंगे और इसे परीक्षणों के लिए मॉक करेंगे, उदाहरण के लिए IGetRelevantDbObjectsQuery), लेकिन कई विशेषताओं और प्रश्नों / आदेशों के साथ एक बड़े कोडबेस के लिए, मैं इनकैप्सुलेशन से प्यार करता हूं और जन्मजात DI समर्थन Mediatr प्रदान करता है।

यदि आप सोच रहे हैं कि मैं इन वर्गों को कैसे व्यवस्थित करूँ, तो यह बहुत आसान है:

- MyProject
  - Features
    - MyFeature
      - Queries
      - Commands
      - Services
      - DependencyConfig.cs (Ninject feature modules)

फ़ीचर स्लाइस द्वारा व्यवस्थित करना बिंदु के बगल में है, लेकिन यह सभी प्रासंगिक / निर्भर कोड को एक साथ रखता है और आसानी से खोजा जा सकता है। सबसे महत्वपूर्ण बात, मैं कमांड / क्वेरी पृथक्करण सिद्धांत का पालन ​​करते हुए क्वेरी बनाम कमांड को अलग करता हूं ।

यह मेरे सभी मानदंडों को पूरा करता है: यह कम-समारोह है, इसे समझना आसान है, और अतिरिक्त छिपे हुए लाभ हैं। उदाहरण के लिए, आप परिवर्तनों को सहेजने का तरीका कैसे संभालते हैं? अब आप एक भूमिका इंटरफ़ेस का उपयोग करके अपने डीबी संदर्भ को सरल कर सकते हैं (IUnitOfWork.SaveChangesAsync()) और एकल रोल इंटरफ़ेस को मॉक करें या आप अपने RequestHandlers के अंदर कमिटिंग / रोलिंग को एनकैप्सुलेट कर सकते हैं - हालांकि आप यह करना पसंद करते हैं कि यह आपके ऊपर है, जब तक यह बनाए रखने योग्य है। उदाहरण के लिए, मुझे एक एकल सामान्य अनुरोध / हैंडलर बनाने का प्रलोभन दिया गया था, जहाँ आप केवल एक EF ऑब्जेक्ट पास करेंगे और इसे सहेज / अपडेट / निकालेंगे - लेकिन आपको यह पूछना होगा कि आपका इरादा क्या है और याद रखें कि यदि आप चाहते थे हैंडलर को दूसरे स्टोरेज प्रोवाइडर / इम्प्लीमेंटेशन के साथ स्वैप करें, आपको संभवतः स्पष्ट कमांड / क्वेश्चन बनाना चाहिए जो यह दर्शाता है कि आप क्या करने का इरादा रखते हैं। अधिक बार नहीं, एक एकल सेवा या सुविधा के लिए कुछ विशिष्ट की आवश्यकता होगी - इससे पहले कि आप इसके लिए एक सामान्य सामान न बनाएं।

कर रहे हैं निश्चित रूप से इस पद्धति के चेतावनियां - आप बहुत दूर एक साधारण पब / उप तंत्र के साथ जा सकते हैं। मैंने अपने कार्यान्वयन को केवल ईएफ-संबंधित कोड को अमूर्त करने तक सीमित कर दिया है, लेकिन साहसी डेवलपर्स ओवरबोर्ड पर जाने के लिए और सब कुछ संदेश-ize का उपयोग करना शुरू कर सकते हैं - कुछ अच्छे कोड समीक्षा प्रथाओं और सहकर्मी समीक्षाओं को पकड़ना चाहिए। यह एक प्रक्रिया मुद्दा है, न कि MediatR के साथ एक समस्या है, इसलिए इस बात का संज्ञान लें कि आप इस पैटर्न का उपयोग कैसे कर रहे हैं।

आप इस बात का एक ठोस उदाहरण चाहते थे कि लोग यूनिट परीक्षण / मॉकिंग कैसे कर रहे हैं और यह एक ऐसा तरीका है जो हमारे प्रोजेक्ट पर हमारे लिए सफलतापूर्वक काम कर रहा है - और यह टीम कितनी खुश है, इसे अपनाना आसान है। आशा है कि ये आपकी मदद करेगा! प्रोग्रामिंग में सभी चीजों के साथ, कई दृष्टिकोण हैं और यह सब इस बात पर निर्भर करता है कि आप क्या हासिल करना चाहते हैं। मैं सादगी, उपयोग में आसानी, रखरखाव और खोज क्षमता को महत्व देता हूं - और यह समाधान उन सभी मांगों को पूरा करता है।


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

3

एफर्ट है जो एक मेमोरी यूनिट फ्रेमवर्क डेटाबेस प्रदाता में है। मैं वास्तव में यह कोशिश नहीं की है ... हा बस देखा यह सवाल में उल्लेख किया गया था!

वैकल्पिक रूप से आप EntityFrameworkCore पर स्विच कर सकते हैं जिसमें अंतर्निहित मेमोरी डेटाबेस प्रदाता है।

https://blog.goyello.com/2016/07/14/save-time-mocking-use-your-real-entity-framework-dbcontext-in-unit-tests/

https://github.com/tamasflamich/effort

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

return new MyContext(@"Server=(localdb)\mssqllocaldb;Database=EFProviders.InMemory;Trusted_Connection=True;");

हाय andrew, इस मुद्दे को संदर्भ कभी नहीं मिल रहा था, आप संदर्भ को फ़ैक्ट कर सकते हैं जो कि हम कर रहे थे, संदर्भ को अमूर्त कर रहे हैं और इसे कारखाने द्वारा बनाया गया है। सबसे बड़ा मुद्दा यह था कि मेमोरी में क्या-क्या था बनाम लिन्क 4इंटिटीज की निरंतरता, वे एक ही नहीं हैं जिससे भ्रामक परीक्षण हो सकते हैं। वर्तमान में, हम परीक्षण डेटाबेस सामान को केवल एकीकरण करते हैं, हो सकता है कि आप सभी के लिए सबसे अच्छी प्रक्रिया न हो।
मोदिका

यदि आप मॉक आउट करना चाहते हैं तो यह Moq हेल्पर काम करता है ( codeproject.com/Tips/1045590/… )। यदि आपकी सूची के साथ नकली-आउट संदर्भ का समर्थन किया जा रहा है तो यह एक वर्ग डेटाबेस द्वारा समर्थित संदर्भ की तरह व्यवहार नहीं करेगा।
andrew pate

2

मुझे अपने फ़िल्टर को कोड के अन्य भागों से अलग करना और उन पर परीक्षण करना पसंद है, जैसा कि मैंने अपने ब्लॉग पर यहाँ उल्लिखित किया हैhttp://coding.grax.com/2013/08/testing-custom-linq-filter-operators.html

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


0

यह परीक्षण करना महत्वपूर्ण है कि आप इकाई ढांचे की अपेक्षा क्या कर रहे हैं (यानी अपनी अपेक्षाओं को मान्य करें)। ऐसा करने का एक तरीका जो मैंने सफलतापूर्वक उपयोग किया है, moq का उपयोग इस उदाहरण में दिखाया गया है (इस उत्तर में कॉपी करने के लिए लंबे समय तक):

https://docs.microsoft.com/en-us/ef/ef6/fundamentals/testing/mocking

हालांकि सावधान रहें ... एक एसक्यूएल संदर्भ किसी विशिष्ट क्रम में चीजों को वापस करने की गारंटी नहीं है जब तक कि आपके लिनेक क्वेरी में एक उपयुक्त "ऑर्डरबाय" न हो, इसलिए जब आप इन-मेमोरी सूची का उपयोग करके परीक्षण करते हैं तो उन चीजों को लिखना संभव है जो पास हो जाते हैं ( linq-to-इकाइयों) लेकिन अपने uat / लाइव वातावरण में विफल (linq-to-sql) का उपयोग किया जाता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.