आज, मैं टेस्ट-ड्रिवन डेवलपमेंट में बिल्डर पैटर्न के बारे में बात करूँगा। यदि आप पहले से ही टेस्ट के साथ काम कर रहे हैं, तो आपने शायद देखा होगा कि सभी इनपुट डेटा बनाने में कितना समय लग सकता है। अक्सर, डेटा का एक ही सेट, या थोड़े अंतर वाले डेटा का उपयोग सिस्टम के टेस्ट सूट में कई परीक्षणों में किया जाता है। बिल्डर यहाँ मदद करता है। यह दो उद्देश्यों को पूरा करता है:
उदाहरण के लिए मैं Invoice
क्लास ले लूँगा, इसका बहुत सरलीकृत संस्करण कुछ इस प्रकार हो सकता है:
public class Invoice { public Invoice( string invoiceNo, string customer, string countryCode, DateTime invoiceDate, IReadOnlyList<InvoiceLine> lines) { InvoiceNo = invoiceNo; InvoiceDate = invoiceDate; Customer = customer; CountryCode = countryCode; Lines = lines; } public string InvoiceNo { get; } public string Customer { get; } public string CountryCode { get; } public DateTime InvoiceDate { get; } public decimal TotalAmount => Lines.Sum(x => x.TotalPrice); public IReadOnlyList<InvoiceLine> Lines { get; } } public class InvoiceLine { public InvoiceLine( string itemCode, decimal unitCount, decimal unitPrice, decimal vat) { ItemCode = itemCode; UnitCount = unitCount; UnitPrice = unitPrice; Vat= vat; } public string ItemCode { get; } public decimal UnitCount { get; } public decimal UnitPrice { get; } public decimal Vat { get; } public decimal TotalPrice => UnitCount * UnitPrice * (1 + Vat / 100); }
Invoice
ऑब्जेक्ट बनाने के लिए, मुझे Invoice
और InvoiceLine
के कंस्ट्रक्टर को कई मान प्रदान करने होंगे। कई मामलों में, गुणों का केवल एक हिस्सा विशिष्ट परीक्षणों के लिए प्रासंगिक होता है। यहाँ, बिल्डर मदद के लिए आते हैं।
InvoiceLine
के लिए बिल्डर कुछ इस तरह दिख सकता है:
public partial class InvoiceLineBuilder { private string _itemCode; private decimal _unitCount; private decimal _unitPrice; private decimal _vat; public static implicit operator InvoiceLine(InvoiceLineBuilder builder) => builder.Build(); public static InvoiceLineBuilder Default() { return new InvoiceLineBuilder( "001", 1, 100, 21 ); } public InvoiceLineBuilder( string itemCode, decimal unitCount, decimal unitPrice, decimal vat) { _itemCode = itemCode; _unitCount = unitCount; _unitPrice = unitPrice; _vat = vat; } public InvoiceLine Build() { return new InvoiceLine( _itemCode, _unitCount, _unitPrice, _vat ); } public InvoiceLineBuilder WithItemCode(string value) { _itemCode = value; return this; } public InvoiceLineBuilder WithUnitCount(decimal value) { _unitCount = value; return this; } public InvoiceLineBuilder WithUnitPrice(decimal value) { _unitPrice = value; return this; } public InvoiceLineBuilder WithVat(decimal vat) { _vat = value; return this; } }
Invoice
के लिए बिल्डर कुछ इस तरह दिख सकता है:
public partial class InvoiceBuilder { private string _invoiceNo; private string _customer; private string _countryCode; private DateTime _invoiceDate; private IReadOnlyList<InvoiceLine> _lines; public static implicit operator Invoice(InvoiceBuilder builder) => builder.Build(); public static InvoiceBuilder Default() { return new InvoiceBuilder( "S001", "AB VeryImportantCustomer", "SV", DateTime.Parse("2024-01-01"), new [] { InvoiceLineBuilder .Default() .Build() } ); } public InvoiceBuilder( string invoiceNo, string customer, string countryCode, DateTime invoiceDate, IReadOnlyList<InvoiceLine> lines) { _invoiceNo = invoiceNo; _customer = customer; _countryCode = countryCode; _invoiceDate = invoiceDate; _lines = lines; } public Invoice Build() { return new Invoice( _invoiceNo, _invoiceDate, _lines ); } public InvoiceBuilder WithInvoiceNo(string value) { _invoiceNo = value; return this; } public InvoiceBuilder WithCustomer(string value) { _customer = value; return this; } public InvoiceBuilder WithCountryCode(string value) { _countryCode = value; return this; } public InvoiceBuilder WithInvoiceDate(DateTime value) { _invoiceDate = value; return this; } public InvoiceBuilder WithLines(IReadOnlyList<InvoiceLine> value) { _lines = value; return this; } public InvoiceBuilder WithLines(params InvoiceLine[] value) { _lines = value; return this; } }
यदि किसी परीक्षण को केवल उसके कुल मूल्य गुण के लिए Invoice
ऑब्जेक्ट की आवश्यकता हो, तो Invoice
इस प्रकार बनाया जा सकता है:
var invoice = InvoiceBuilder .Default() .WithLines( InvoiceLineBuilder .Default .WithUnitPrice(158) );
चूंकि कुल कीमत की गणना इनवॉइस लाइनों को जोड़कर की जाती है, और इनवॉइस लाइन के लिए डिफ़ॉल्ट यूनिट काउंट 1 है, तो इनवॉइस लाइन के लिए यूनिट मूल्य निर्धारित करना पर्याप्त है। यदि कई परीक्षणों में समान कार्यक्षमता की आवश्यकता है, तो हम आगे बढ़ सकते हैं और InvoiceBuilder
में निम्नलिखित विधि जोड़ सकते हैं:
public static InvoiceBuilder DefaultWithTotalPrice(decimal totalPrice) { return new InvoiceBuilder( "S001", DateTime.Parse("2023-01-01"), new[] { InvoiceLineBuilder .Default() .WithUnitPrice(totalPrice) .Build() } ); }
जैसा कि ऊपर बताया गया है, बिल्डर क्लास क्लास के लिए सभी कॉमन और एज केस को इकट्ठा करने के लिए एक बेहतरीन जगह है। यहाँ, मैं उन संभावित मामलों में से कुछ प्रदान करूँगा:
मेरे दृष्टिकोण से, यह हमारे सिस्टम द्वारा संभाले जाने वाले विभिन्न मामलों के बारे में जानकारी एकत्र करने के लिए एक बेहतरीन जगह है। यह नए डेवलपर्स के लिए एक उपयोगी ज्ञान आधार के रूप में कार्य करता है ताकि वे समझ सकें कि सिस्टम को क्या प्रबंधित करने की आवश्यकता है। अगर मैं किसी क्षेत्र में नया हूँ, तो मैं संभावित एज केस के बारे में सोच भी नहीं सकता। यहाँ ऊपर बताए गए कुछ मामलों का एक कोड उदाहरण दिया गया है:
public static InvoiceBuilder ForEUCountry() { return Default() .WithCountryCode("SV"); } public static InvoiceBuilder ForUSA() { return Default() .WithCountryCode("USA"); } public static InvoiceBuilder ForChina() { return Default() .WithCountryCode("CN"); } public InvoiceBuilder WithRegularVat() { return this .WithLines( InvoiceLineBuilder .Default .WithItemCode("S001") .WithVat(21), InvoiceLineBuilder .Default .WithItemCode("S002") .WithVat(21) ); } public InvoiceBuilder WithReducedVat() { return this .WithLines( InvoiceLineBuilder .Default .WithItemCode("S001") .WithVat(9), InvoiceLineBuilder .Default .WithItemCode("S002") .WithVat(9) ); } public InvoiceBuilder WithMixedVat() { return this .WithLines( InvoiceLineBuilder .Default .WithItemCode("S001") .WithVat(21), InvoiceLineBuilder .Default .WithItemCode("S002") .WithVat(9) ); }
अब हम उपरोक्त का मिश्रण बना सकते हैं। उदाहरण के लिए, यदि किसी परीक्षण मामले में किसी EU ग्राहक के लिए चालान की आवश्यकता है, जिसमें मिश्रित VAT वाली चालान लाइनें हों, तो मैं निम्न कार्य कर सकता हूँ:
[Test] public void SomeTest() { //arrange var invoice = InvoiceBuilder .ForEU() .WithMixedVat(); //act ... //assert ... }
यह एक सरल उदाहरण है, लेकिन मुझे आशा है कि आप अवधारणा को समझ जायेंगे।
बिल्डर तब उपयोगी होता है जब हमारे पास एक बड़ी, जटिल वस्तु होती है, लेकिन परीक्षण के लिए केवल कुछ ही क्षेत्र प्रासंगिक होते हैं।
एक और उपयोगी मामला तब होता है जब मैं विशिष्ट मानों के आधार पर कई परिदृश्यों का परीक्षण करना चाहता हूँ। एक को छोड़कर सभी गुण समान रहते हैं, और मैं केवल एक को बदलता हूँ। इससे अंतर को उजागर करना आसान हो जाता है, जिसके कारण सेवा या ऑब्जेक्ट अलग तरह से व्यवहार करता है।
सबसे पहले, आप खुद ही एक बिल्डर क्लास बना सकते हैं। इसके लिए समय या पैसे के किसी शुरुआती निवेश की आवश्यकता नहीं होती है, और आपको इसे बनाने के तरीके में बहुत स्वतंत्रता होती है। कॉपी करना, चिपकाना और बदलना उपयोगी हो सकता है, लेकिन इसमें अभी भी काफी समय लगता है, खासकर बड़ी कक्षाओं के लिए।
जब मैंने कोड जनरेशन के साथ शुरुआत की, तो मैंने इसके लिए एक सिंगल टेस्ट सेट अप करके शुरुआत की। इस टेस्ट ने वास्तव में कुछ भी टेस्ट नहीं किया; इसने केवल एक प्रकार को स्वीकार किया, रिफ्लेक्शन का उपयोग करके सभी गुणों को पुनर्प्राप्त किया, हार्डकोडेड टेम्पलेट से एक बिल्डर क्लास बनाया, और इसे टेस्ट रनर आउटपुट विंडो में लिखा। मुझे बस एक क्लास फ़ाइल बनानी थी और टेस्ट रनर की आउटपुट विंडो से सामग्री को कॉपी/पेस्ट करना था।
BuilderGenerator के बारे में सब कुछ यहाँ पाया जा सकता है। यह .NET इंक्रीमेंटल सोर्स जेनरेटर के बारे में बताता है। इसका मतलब है कि जब लक्ष्य वर्ग बदलता है तो बिल्डर कोड लाइव रीजेनरेट हो जाता है। इसलिए, ऊपर दिए गए तरीकों की तुलना में कोई परेशानी या मैन्युअल काम नहीं है। बस एक बिल्डर वर्ग बनाएँ, लक्ष्य वर्ग प्रकार के साथ BuilderFor
विशेषता जोड़ें, और सभी With
विधियाँ स्वचालित रूप से उत्पन्न होती हैं और उपयोग के लिए तैयार होती हैं।
[BuilderFor(typeof(InvoiceLine))] public partial class InvoiceLineBuilder { public static InvoiceLineBuilder Default() { return new InvoiceLineBuilder() .WithItemCode("S001") .WithUnitCount(1); } }
मैंने इसके साथ ज़्यादा काम नहीं किया है, लेकिन ऐसा लगता है कि इसका उपयोगकर्ता आधार काफ़ी बड़ा है और लिखते समय तक इसके 82.7K डाउनलोड हो चुके हैं। मैंने कुछ मुद्दे देखे, जिसके कारण मुझे दूसरे विकल्प चुनने पड़े:
यदि बिल्डर क्लास लक्ष्य क्लास से भिन्न प्रोजेक्ट में है तो समाधान बिल्ड करने में विफल रहता है। यह किसी अन्य प्रोजेक्ट में हो सकता है, लेकिन नामस्थान वही रहना चाहिए। अन्यथा, आपको निम्न त्रुटियाँ दिखाई देंगी::
यह कन्स्ट्रक्टर पैरामीटर्स का समर्थन नहीं करता है और यदि लक्ष्य वर्ग में पैरामीटर रहित कन्स्ट्रक्टर नहीं है तो यह त्रुटियों के साथ विफल हो जाता है।
आइये देखें कि हमारे पास और क्या विकल्प हैं।
यह एक बहुत ही लोकप्रिय लाइब्रेरी है, जिसके लेखन के समय कुल 82.2 मिलियन से अधिक डाउनलोड (और वर्तमान संस्करण के लिए 186.1K) हैं। जैसा कि लाइब्रेरी के लेखक ने कहा है, यह एक नकली डेटा जनरेटर है जो पूर्वनिर्धारित नियमों के आधार पर कई ऑब्जेक्ट बनाने में सक्षम है। यह बिल्कुल वैसा नहीं है जैसा कि TDD में बिल्डर पैटर्न है, लेकिन इसे अनुकूलित किया जा सकता है।
Bogus.Faker का उपयोग करने के कई तरीके हैं, लेकिन मैं यहां बिल्डर पैटर्न की नकल करने पर ध्यान केंद्रित करूंगा।
Bogus.Faker के साथ ऑब्जेक्ट बनाने का सबसे सरल तरीका यह है:
[Test] public void BogusTest() { var faker = new Faker<InvoiceLine2>(); var invoiceLine = faker.Generate(); Assert.IsNotNull(invoiceLine); }
यह डिफ़ॉल्ट मानों के साथ InvoiceLine2
का एक उदाहरण बनाता है, जिसका अर्थ है शून्य और शून्य। कुछ मान सेट करने के लिए, मैं निम्नलिखित सेटअप का उपयोग करूँगा:
[Test] public void BogusTest() { var faker = new Faker<InvoiceLine2>() .RuleFor(x => x.ItemCode, f => f.Random.AlphaNumeric(5)) .RuleFor(x => x.UnitPrice, f => f.Random.Decimal(10, 1000)) .RuleFor(x => x.UnitCount, f => f.Random.Number(1, 5)) .RuleFor(x => x.Vat, f => f.PickRandom(21, 9, 0)); var invoiceLine = faker.Generate(); Assert.IsNotNull(invoiceLine); ToJson(invoiceLine); }
ऊपर दिया गया कोड यादृच्छिक मानों के साथ एक इनवॉइस लाइन ऑब्जेक्ट बनाता है। एक उदाहरण इस तरह दिख सकता है:
{ "ItemCode": "gwg7y", "UnitCount": 3.0, "UnitPrice": 597.035612417891230, "Vat": 0.0, "TotalPrice": 1791.106837253673690 }
यह उपयोगी है, लेकिन प्रत्येक परीक्षण के लिए अपने स्वयं के सेटअप की आवश्यकता होती है। इसके बजाय, हम एक बिल्डर क्लास बना सकते हैं:
public class InvoiceLineBuilder: Faker<InvoiceLine2> { public static InvoiceLineBuilder Default() { var faker = new InvoiceLineBuilder(); faker .RuleFor(x => x.ItemCode, f => f.Random.AlphaNumeric(5)) .RuleFor(x => x.UnitPrice, f => f.Random.Decimal(10, 1000)) .RuleFor(x => x.UnitCount, f => f.Random.Number(1, 5)) .RuleFor(x => x.Vat, f => f.PickRandom(21, 9, 0)); return faker; } }
इसका उपयोग कुछ इस प्रकार होगा:
[Test] public void BogusTest() { var faker = TestDoubles.Bogus.InvoiceLineBuilder .Default() .RuleFor(x => x.ItemCode, f => "S001") .RuleFor(x => x.UnitPrice, f => 100); var invoiceLine = faker.Generate(); Assert.IsNotNull(invoiceLine); ToJson(invoiceLine); }
और आउटपुट:
{ "ItemCode": "S001", "UnitCount": 2.0, "UnitPrice": 100.0, "Vat": 9.0, "TotalPrice": 218.00 }
मेरे दृष्टिकोण से, यह नियमित बिल्डर पैटर्न की तुलना में थोड़ा अधिक विस्तृत है। इसके अतिरिक्त, मैं यादृच्छिक मानों का उपयोग करने का प्रशंसक नहीं हूँ। यह कोई बड़ी समस्या नहीं है, लेकिन समस्या तब उत्पन्न होती है जब किसी क्लास के गुणों को कंस्ट्रक्टर का उपयोग करके आरंभ किया जाता है और इसमें सेटर्स नहीं होते हैं। तब यह बिल्डर के रूप में काम नहीं करता है, और प्रत्येक सेटअप स्थिर हो जाता है।
var faker = new InvoiceLineBuilder(); faker .CustomInstantiator(f => new InvoiceLine( f.Random.AlphaNumeric(5), f.Random.Decimal(10, 1000), f.Random.Number(1, 5), f.PickRandom(21, 9, 0) ) );
यह भी एक बहुत लोकप्रिय लाइब्रेरी है जिसके कुल 13.2 मिलियन से ज़्यादा डाउनलोड हैं (और मौजूदा वर्शन के लिए 7.2 मिलियन)। हालाँकि इसे हाल ही में सक्रिय रूप से विकसित नहीं किया गया है, लेकिन इसका आखिरी वर्शन 2019 में रिलीज़ किया गया था। अनिवार्य रूप से, यह Bogus.Faker से काफ़ी मिलता-जुलता है। एक विशिष्ट IPropertyNamer को लागू करके यादृच्छिक मान प्रदान करने के लिए Bogus का फिर से उपयोग करना भी संभव होना चाहिए।
आइये बिना कोई गुण सेट किये इसका प्रयोग करके देखें:
[Test] public void NBuilderTest() { var invoiceLine = Builder<InvoiceLine2> .CreateNew() .Build(); Assert.IsNotNull(invoiceLine); ToJson(invoiceLine); }
यह निम्नलिखित आउटपुट उत्पन्न करता है::
{ "ItemCode": "ItemCode1", "UnitCount": 1.0, "UnitPrice": 1.0, "Vat": 1.0, "TotalPrice": 1.01 }
इस पोस्ट का उद्देश्य यह दिखाना है कि पुनः उपयोग योग्य बिल्डर क्लास कैसे बनाया जाए। चलिए शुरू करते हैं:
public class InvoiceLineBuilder { public static ISingleObjectBuilder<InvoiceLine2> Default() { return Builder<InvoiceLine2> .CreateNew() .With(x => x.ItemCode, "S001") .With(x => x.UnitCount, 1) .With(x => x.UnitPrice, 100) .With(x => x.Vat, 21); } }
और इसका उपयोग इस प्रकार है:
[Test] public void NBuilderTest() { var invoiceLine = TestDoubles.NBuilder.InvoiceLineBuilder .Default() .With(x => x.ItemCode, "S002") .With(x => x.Vat, 9) .Build(); Assert.IsNotNull(invoiceLine); ToJson(invoiceLine); }
और आउटपुट:
{ "ItemCode": "S002", "UnitCount": 1.0, "UnitPrice": 100.0, "Vat": 9.0, "TotalPrice": 109.00 }
Bogus.Faker की तरह, यदि क्लास प्रॉपर्टी को कन्स्ट्रक्टर का उपयोग करके सेट किया गया है और उसमें सेटर नहीं है, तो आप मानों को ओवरराइड नहीं कर सकते। यदि आप ऐसी प्रॉपर्टी के लिए With विधि का उपयोग करने का प्रयास करते हैं, तो यह निम्नलिखित अपवाद के साथ विफल हो जाएगा:
System.ArgumentException : Property set method not found.
EasyTdd.Generators.Builder एक Nuget पैकेज है और EasyTdd - Visual Studio एक्सटेंशन के साथ मिलकर काम करता है। यह पैकेज EasyTdd एक्सटेंशन द्वारा उपयोग किए जाने वाले टेम्प्लेट से बिल्डर बनाने के लिए .NET वृद्धिशील स्रोत जनरेटर का लाभ उठाता है। बिल्डर जनरेटर प्रॉपर्टी सेटर्स, कंस्ट्रक्टर पैरामीटर और दोनों के संयोजन को संभालता है। यह जेनेरिक पैरामीटर का भी समर्थन करता है।
बिल्डर बनाने का यह मेरा पसंदीदा तरीका है। अन्य विकल्पों की तुलना में इसके लाभ इस प्रकार हैं:
जब Visual Studio में EasyTdd स्थापित हो जाए, तो लक्ष्य वर्ग पर त्वरित क्रिया मेनू खोलें, और "Incremental Builder उत्पन्न करें" का चयन करें:
यह क्रिया BuilderFor विशेषता सेट के साथ एक आंशिक बिल्डर वर्ग बनाती है:
[EasyTdd.Generators.BuilderFor(typeof(InvoiceLine))] public partial class InvoiceLineBuilder { public static InvoiceLineBuilder Default() { return new InvoiceLineBuilder( () => default, // Set default itemCode value () => default, // Set default unitCount value () => default, // Set default unitPrice value () => default // Set default vat value ); } }
बिल्डर कोड स्वयं पृष्ठभूमि में उत्पन्न होता है, और यह आंशिक वर्ग सामान्य/एज केस सेटअप के लिए अभिप्रेत है। default
के बजाय डिफ़ॉल्ट मान सेट करने के लिए स्वतंत्र महसूस करें।
इसे स्थापित करने और यह कैसे काम करता है, इसके बारे में अधिक जानकारी यहां पाई जा सकती है।
अच्छी बात यह है कि अगर मुझे यादृच्छिक मानों की आवश्यकता होती है, तो मैं यहां बोगस का उपयोग कर सकता हूं:
public static InvoiceLineBuilder Random() { var f = new Faker(); return new InvoiceLineBuilder( () => f.Random.AlphaNumeric(5), () => f.Random.Decimal(10, 1000), () => f.Random.Number(1, 5), () => f.PickRandom(21, 9, 0) ); }
उपयोग:
[Test] public void EasyTddBuilder() { var invoiceLine = TestDoubles.Builders.InvoiceLineBuilder .Random() .WithUnitPrice(100) .WithUnitCount(1) .Build(); Assert.IsNotNull(invoiceLine); ToJson(invoiceLine); }
और आउटपुट:
{ "ItemCode": "ana0i", "UnitCount": 1.0, "UnitPrice": 100.0, "Vat": 9.0, "TotalPrice": 109.00 }
EasyTdd, EasyTdd.Generators Nuget पैकेज पर निर्भरता के बिना पूर्ण बिल्डर कोड जनरेशन भी प्रदान करता है। यह तब उपयोगी होता है जब आप थर्ड-पार्टी लाइब्रेरी पर निर्भर नहीं रहना चाहते या आपको इसकी अनुमति नहीं है। एक्सटेंशन कोड जनरेट करता है और सब कुछ आपके प्रोजेक्ट में होता है, बिना किसी निर्भरता या स्ट्रिंग के। इसे संशोधित करने के लिए स्वतंत्र महसूस करें, यह सब आपका है। यह दृष्टिकोण EasyTdd.Generators मामले के सभी लाभ प्रदान करता है, सिवाय लक्ष्य वर्ग परिवर्तनों पर स्वचालित पुनर्जनन के।
इस मामले में, बिल्डर को मैन्युअल रूप से पुनर्जीवित करने की आवश्यकता है (कुछ क्लिक के साथ भी)। पुनर्जनन पर सेटअप खोने से बचने के लिए दो फ़ाइलें उत्पन्न की जाती हैं। एक फ़ाइल में बिल्डर क्लास की घोषणा होती है, जिसमें सभी आवश्यक विधियाँ होती हैं, दूसरी केवल सेटअप और अतिरिक्त विधियों के लिए होती है, जो पुनर्जनन के लिए अभिप्रेत नहीं होती हैं। क्लास को ऊपर बताए गए तरीके से ही उत्पन्न किया जा सकता है, त्वरित कार्रवाई मेनू खोलकर और "बिल्डर उत्पन्न करें" पर क्लिक करके:
जब बिल्डर पहले से ही उत्पन्न हो जाता है तो उपकरण बिल्डर वर्ग को खोलने या पुन: उत्पन्न करने की पेशकश करता है:
इस ब्लॉग पोस्ट में, मैंने बिल्डर पैटर्न और टेस्ट-ड्रिवन डेवलपमेंट में इसके उपयोग का परिचय दिया। मैंने इसे लागू करने के कई तरीके भी दिखाए, जिसमें मैन्युअल कार्यान्वयन से लेकर, Bogus.Faker और NBuilder जैसी थर्ड-पार्टी लाइब्रेरी का उपयोग, BuilderGenerator और EasyTdd.Generators.Builder जैसे वृद्धिशील कोड जनरेटर और अंत में, EasyTdd Visual Studio एक्सटेंशन द्वारा सभी कोड जेनरेट करना शामिल है। प्रत्येक विधि की अपनी ताकत और कमजोरियाँ हैं और सरल मामलों में अच्छी तरह से काम करती हैं।
हालाँकि, अपरिवर्तनीय वर्गों से निपटने के दौरान, EasyTdd संपत्ति परिवर्तनों को समान रूप से संभालने में सबसे आगे रहता है, चाहे संपत्ति का मूल्य किसी सेटर द्वारा आरंभ किया गया हो या किसी कंस्ट्रक्टर पैरामीटर के माध्यम से। EasyTdd टेम्प्लेट का समर्थन करता है और आपको अपनी प्राथमिकताओं से मेल खाने के लिए आउटपुट को अनुकूलित करने की अनुमति देता है। EasyTdd अपने कार्यान्वयन की गति के कारण बिल्डर को लागू करने के अन्य तरीकों से भी आगे है। यह Visual Studio में कुछ ही क्लिक के साथ स्वचालित रूप से फ़ाइलें उत्पन्न करने के लिए उपकरण प्रदान करता है, जिससे समय और प्रयास की बचत होती है।
अनस्प्लैश पर मार्कस स्पिस्के द्वारा फोटो