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