8

जब मैं Moq का उपयोग सीधे IBuilderFactory नकली और BuilderService अपने आप को एक इकाई परीक्षण में दृष्टांत के लिए, मैं एक गुजर परीक्षण है जो पुष्टि करता है कि IBuilderFactory की Create() विधि ठीक एक बार कहा जाता है प्राप्त कर सकते हैं।ऑटोफिक्शन डब्ल्यू/ऑटोमोक कस्टमाइज़ेशन क्लास सील होने पर पैरामीटर रहित कन्स्ट्रक्टर की कमी के बारे में शिकायत क्यों रोकता है?

System.ArgumentException:: की प्रॉक्सी का दृष्टांत नहीं कर सकते

हालांकि, जब मैं Autofixture का उपयोग AutoMoqCustomization साथ, IBuilderFactory की एक नकली ठंड और fixture.Create<BuilderService> साथ BuilderService instantiating, मैं निम्नलिखित अपवाद कक्षा: OddBehaviorTests.CubeBuilder। पैरामीटर रहित कन्स्ट्रक्टर नहीं मिला। पैरामीटर नाम: constructorArguments

अगर मैं CubeBuilder सील (, मोहरबंद वर्ग SealedCubeBuilder जो IBuilderFactoryForSealedBuilder.Create() द्वारा कहा जाता है, परीक्षण AutoMoqCustomization साथ AutoFixture का उपयोग कर गुजरता है और इसकी जगह कोई अपवाद नहीं फेंक दिया साथ का प्रतिनिधित्व करती

Am बनाते हैं। मुझे कुछ याद आ रहा है? चूंकि मैं सीधे मक का उपयोग करके परीक्षण पास कर रहा हूं, मेरा मानना ​​है कि यह ऑटोफिक्चर और/या ऑटोमोक कस्टमाइजेशन से संबंधित है। क्या यह वांछित व्यवहार है? यदि हां, तो क्यों?

पुन: उत्पन्न करने के लिए, मैं इसका उपयोग कर रहा हूं:

public class BuilderServiceTests { 
    [Fact] 
    public void CubeBuilderFactoryCreateMethodShouldBeCalled_UsingMoq() { 
     var factory = new Mock<IBuilderFactory>(); 
     var sut = new BuilderService(factory.Object); 
     sut.Create(); 
     factory.Verify(f => f.Create(), Times.Once()); 
    } 
    [Fact] 
    public void CubeBuilderFactoryCreateMethodShouldBeCalled_UsingAutoFixture() { 
     var fixture = new Fixture().Customize(new AutoMoqCustomization()); 
     var factory = fixture.Freeze<Mock<IBuilderFactory>>(); 
     var sut = fixture.Create<BuilderService>(); 
     sut.Create(); // EXCEPTION THROWN!! 
     factory.Verify(f => f.Create(), Times.Once()); 
    } 
    [Fact] 
    public void SealedCubeBuilderFactoryCreateMethodShouldBeCalled_UsingMoq() { 
     var factory = new Mock<IBuilderFactoryForSealedBuilder>(); 
     var sut = new BuilderServiceForSealedBuilder(factory.Object); 
     sut.Create(); 
     factory.Verify(f => f.Create(), Times.Once()); 
    } 
    [Fact] 
    public void SealedCubeBuilderFactoryCreateMethodShouldBeCalled_UsingAutoFixture() { 
     var fixture = new Fixture().Customize(new AutoMoqCustomization()); 
     var factory = fixture.Freeze<Mock<IBuilderFactoryForSealedBuilder>>(); 
     var sut = fixture.Create<BuilderServiceForSealedBuilder>(); 
     sut.Create(); 
     factory.Verify(f => f.Create(), Times.Once()); 
    } 
} 

यहाँ आवश्यक वर्गों रहे हैं:

using Moq; 
using Ploeh.AutoFixture; 
using Ploeh.AutoFixture.AutoMoq; 
using Xunit; 

यहाँ व्यवहार को दर्शाता हुआ चार टेस्ट कर रहे हैं

public interface IBuilderService { IBuilder Create(); } 
public class BuilderService : IBuilderService { 
    private readonly IBuilderFactory _factory; 
    public BuilderService(IBuilderFactory factory) { _factory = factory; } 
    public IBuilder Create() { return _factory.Create(); } 
} 
public class BuilderServiceForSealedBuilder : IBuilderService { 
    private readonly IBuilderFactoryForSealedBuilder _factory; 
    public BuilderServiceForSealedBuilder(IBuilderFactoryForSealedBuilder factory) { _factory = factory; } 
    public IBuilder Create() { return _factory.Create(); } 
} 

public interface IBuilderFactoryForSealedBuilder { SealedCubeBuilder Create(); } 
public interface IBuilderFactory { CubeBuilder Create(); } 
public interface IBuilder { void Build(); } 

public abstract class Builder : IBuilder { 
    public void Build() { } // build stuff 
} 

public class CubeBuilder : Builder { 
    private Cube _cube; 
    public CubeBuilder(Cube cube) { _cube = cube; } 
} 

public sealed class SealedCubeBuilder : Builder { 
    private Cube _cube; 
    public SealedCubeBuilder(Cube cube) { _cube = cube; } 
} 

public class Cube { } 

उत्तर

11

आप स्टैक ट्रेस को देखें, तो आपको लगता है कि अपवाद पर ध्यान देंगे मोक के भीतर गहरा होता है। ऑटोफिक्चर एक राय वाली लाइब्रेरी है, और इसमें से एक राय यह है कि नल अमान्य रिटर्न मान हैं। (अन्य बातों के अलावा)

mock.DefaultValue = DefaultValue.Mock; 

: इसी कारण से, AutoMoqCustomization सब नकली उदाहरणों इस तरह कॉन्फ़िगर करता है। इस प्रकार, आप AutoFixture बिना पूरी तरह से नाकाम रहने के परीक्षण पुन: पेश कर सकते हैं:

[Fact] 
public void ReproWithoutAutoFixture() 
{ 
    var factory = new Mock<IBuilderFactory>(); 
    factory.DefaultValue = DefaultValue.Mock; 
    var sut = new BuilderService(factory.Object); 
    sut.Create(); // EXCEPTION THROWN!! 
    factory.Verify(f => f.Create(), Times.Once()); 
} 

अजीब बात यह अभी भी सील कर दिया वर्गों के साथ काम करने के लिए लगता है कि है। हालांकि, यह काफी सच नहीं है, बल्कि ओपी परीक्षणों में False Negatives होने का अनुमान है।

Moq के इस Characterization Test पर विचार करें:

[Fact] 
public void MoqCharacterizationForUnsealedClass() 
{ 
    var factory = new Mock<IBuilderFactory>(); 
    factory.DefaultValue = DefaultValue.Mock; 
    Assert.Throws<ArgumentException>(() => factory.Object.Create()); 
} 

Moq सही ढंग से एक अपवाद फेंकता है, क्योंकि यह CubeBuilder का एक उदाहरण बनाने के लिए कहा गया है, और यह कैसे है कि, CubeBuilder नहीं है क्योंकि ऐसा करने के लिए पता नहीं है डिफ़ॉल्ट कन्स्ट्रक्टर, और Setup यह बताता है कि Create पर कॉल से कैसे निपटें।

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

अब इस विशेषता परीक्षण मानते हैं कि वापसी प्रकार सील:

[Fact] 
public void MoqCharacterizationForSealedClass() 
{ 
    var factory = new Mock<IBuilderFactoryForSealedBuilder>(); 
    factory.DefaultValue = DefaultValue.Mock; 
    var actual = factory.Object.Create(); 
    Assert.Null(actual); 
} 

ऐसा लगता है कि इस मामले में, के बावजूद परोक्ष होने null, Moq तो वैसे भी करता है वापस जाने के लिए नहीं कहा था। दूसरे शब्दों में, यह गतिशील रूप से एक वर्ग का उत्सर्जन करता है कि CubeBuilder से निकला -

मेरे सिद्धांत यह है कि क्या वास्तव में हो रहा है ऊपर MoqCharacterizationForUnsealedClass में, क्या factory.DefaultValue = DefaultValue.Mock; वास्तव में इसका मतलब है Moq CubeBuilder के नकली बनाता है वह यह है कि वह यह है कि है । हालांकि, जब SealedCubeBuilder के नकली बनाने के लिए कहा गया, तो ऐसा नहीं हो सकता है, क्योंकि यह एक सीलबंद कक्षा से प्राप्त कक्षा नहीं बना सकता है।

अपवाद फेंकने के बजाय, यह null देता है। यह असंगत व्यवहार है, और I've reported this as a bug in Moq है।

+0

इस को समझने के लिए धन्यवाद! मिश्रित भावनाएं जब उत्तर लाइब्रेरी में एक बग बन जाता है जिसका उपयोग मैं कर रहा हूं ... मैं इस पर दुर्घटनाग्रस्त हो गया क्योंकि मेरे कुछ XXX बिल्डर वर्गों को बंद कर दिया गया था, कुछ नहीं। इस बारे में तर्क प्राप्त हुआ कि क्या सील नहीं करने के लिए अच्छे कारणों की अनुपस्थिति में कक्षाओं को सील करना है या विपरीत करना है। लगता है कि सीलिंग कक्षाएं इकाई परीक्षण घर्षण का कारण बन सकती हैं - इस मामले पर राय साझा करने की देखभाल? – Jeff

+1

धारणा है कि सीलबंद वर्गों ने टेस्टेबिलिटी को चोट पहुंचाई है (मुझे लगता है) जावा से, जहां सभी सदस्य डिफ़ॉल्ट रूप से आभासी होते हैं। सी # में यह मामला नहीं है, इसलिए सवाल कम महत्वपूर्ण हो जाता है। किसी भी मामले में, यदि आप * विरासत पर संरचना का पक्ष लेते हैं * यह शायद ही मायने रखता है। एफडब्ल्यूआईडब्लू, एफ # निर्माण जैसे कि सीलबंद कक्षाओं के लिए संकलन संकलित, लेकिन एफ # कोड बहुत कम परीक्षण योग्य नहीं हो सकता है। आखिरकार, यदि आप टीडीडी का पालन करते हैं, तो आपके पास टेस्टेबल कोड होगा :) –

+0

मुझे यह स्पष्ट करना चाहिए कि मुहरबंद कक्षाएं टेस्टेबिलिटी को चोट पहुंचाने लगती हैं जब उपयोग में मॉकिंग फ्रेमवर्क सीलबंद कक्षाओं का नकल करने में असमर्थ है, फिर भी ऐसा करने के लिए कहा जा रहा है। फिर एक निश्चित 'पहुंच' सुविधा को लागू करने के बीच चयन करना छोड़ दिया जाता है, [प्रमुख लोगों के रूप में] [http://stackoverflow.com/a/2165287/533958), वर्ग को गैर-मुहरबंद बनाकर बस * मॉकिंग टूल * इसकी आवश्यकता है, और वर्ग को सील कर रखा गया है इसलिए किसी को विरासत के लिए डिजाइन की आवश्यकता नहीं है। हालांकि, टीडीडी विकास के संबंध में लिया गया बिंदु। – Jeff

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

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