2011-09-02 16 views
150

साथ पहले और दूसरे समय को महत्व देता है मैं इस तरह एक परीक्षण है:अलग वापसी Moq

[TestCase("~/page/myaction")] 
    public void Page_With_Custom_Action(string path) { 
     // Arrange 
     var pathData = new Mock<IPathData>(); 
     var pageModel = new Mock<IPageModel>(); 
     var repository = new Mock<IPageRepository>(); 
     var mapper = new Mock<IControllerMapper>(); 
     var container = new Mock<IContainer>(); 

     container.Setup(x => x.GetInstance<IPageRepository>()).Returns(repository.Object); 

     repository.Setup(x => x.GetPageByUrl<IPageModel>(path)).Returns(() => pageModel.Object); 

     pathData.Setup(x => x.Action).Returns("myaction"); 
     pathData.Setup(x => x.Controller).Returns("page"); 

     var resolver = new DashboardPathResolver(pathData.Object, repository.Object, mapper.Object, container.Object); 

     // Act 
     var data = resolver.ResolvePath(path); 

     // Assert 
     Assert.NotNull(data); 
     Assert.AreEqual("myaction", data.Action); 
     Assert.AreEqual("page", data.Controller); 
    } 

GetPageByUrl मेरी dashboardpathresolver में दो बार चलाता है, मैं कैसे बता सकते हैं Moq अशक्त पहली बार और pageModel.Ojbect वापस जाने के लिए दूसरा?

उत्तर

27

एक कॉलबैक मेरे लिए काम नहीं किया जोड़ना, मैं इस दृष्टिकोण के बजाय http://haacked.com/archive/2009/09/29/moq-sequences.aspx का इस्तेमाल किया और मैं इस तरह के परीक्षण के साथ समाप्त हो गया:

[TestCase("~/page/myaction")] 
    [TestCase("~/page/myaction/")] 
    public void Page_With_Custom_Action(string virtualUrl) { 

     // Arrange 
     var pathData = new Mock<IPathData>(); 
     var pageModel = new Mock<IPageModel>(); 
     var repository = new Mock<IPageRepository>(); 
     var mapper = new Mock<IControllerMapper>(); 
     var container = new Mock<IContainer>(); 

     container.Setup(x => x.GetInstance<IPageRepository>()).Returns(repository.Object); 
     repository.Setup(x => x.GetPageByUrl<IPageModel>(virtualUrl)).ReturnsInOrder(null, pageModel.Object); 

     pathData.Setup(x => x.Action).Returns("myaction"); 
     pathData.Setup(x => x.Controller).Returns("page"); 

     var resolver = new DashboardPathResolver(pathData.Object, repository.Object, mapper.Object, container.Object); 

     // Act 
     var data = resolver.ResolvePath(virtualUrl); 

     // Assert 
     Assert.NotNull(data); 
     Assert.AreEqual("myaction", data.Action); 
     Assert.AreEqual("page", data.Controller); 
    } 
20

जब आपकी नकली वस्तु की स्थापना एक कॉलबैक का उपयोग कर सकते हैं। मोक विकी (http://code.google.com/p/moq/wiki/QuickStart) से उदाहरण देखें।

// returning different values on each invocation 
var mock = new Mock<IFoo>(); 
var calls = 0; 
mock.Setup(foo => foo.GetCountThing()) 
    .Returns(() => calls) 
    .Callback(() => calls++); 
// returns 0 on first invocation, 1 on the next, and so on 
Console.WriteLine(mock.Object.GetCountThing()); 

आपका सेटअप इस प्रकार दिखाई देंगे:

var pageObject = pageModel.Object; 
repository.Setup(x => x.GetPageByUrl<IPageModel>(path)).Returns(() => pageObject).Callback(() => 
      { 
       // assign new value for second call 
       pageObject = new PageModel(); 
      }); 
+0

जब मैं ऐसा करता हूं तो मुझे शून्य मिल जाता है: var pageModel = new Mock (); आईपेज मॉडल मॉडल = शून्य; । repository.Setup (x => x.GetPageByUrl (पथ)) रिटर्न्स (() => मॉडल) .Callback (() => { मॉडल = pageModel.Object; }); – Marcus

+0

क्या GetPageByUrl को हलकर्ता के भीतर दो बार बुलाया जाता है। ResolvePath विधि? – Dan

+0

हां इसे दो बार – Marcus

92

मौजूदा उत्तर बहुत अच्छे हैं, लेकिन मैंने सोचा कि मैं अपने विकल्प में फेंक दूंगा जो सिर्फ System.Collections.Generic.Queue का उपयोग करता है और मॉकिंग फ्रेमवर्क के किसी भी विशेष ज्ञान की आवश्यकता नहीं है - क्योंकि मेरे पास कोई समय नहीं था मैने यह लिखा! :)

var pageModel = new Mock<IPageModel>(); 
IPageModel pageModelNull = null; 
var pageModels = new Queue<IPageModel>(); 
pageModels.Enqueue(pageModelNull); 
pageModels.Enqueue(pageModel.Object); 

फिर ...

repository.Setup(x => x.GetPageByUrl<IPageModel>(path)).Returns(pageModels.Dequeue); 
+0

धन्यवाद। मैंने बस टाइपो को ठीक किया जहां मैं पृष्ठ के बजाय मॉडल मॉडल को एनक्यूइंग कर रहा था मॉडल। ऑब्जेक्ट, तो अब इसे भी बनाना चाहिए! :) –

+3

उत्तर सही है, लेकिन ध्यान दें कि यदि आप 'अपवाद' को फेंकना चाहते हैं तो यह काम नहीं करेगा क्योंकि आप इसे 'एनक्यू' नहीं कर सकते हैं। लेकिन 'सेटअपअपेंस' काम करेगा (उदाहरण के लिए @ स्टैकunderफ्लो से जवाब देखें)। – Halvard

+2

आपको डेक्यू के लिए एक प्रतिनिधि विधि का उपयोग करना होगा। नमूना लिखने का तरीका यह हमेशा कतार में पहली वस्तु को बार-बार वापस कर देगा, क्योंकि सेटअप के समय डेक्यू का मूल्यांकन किया जाता है। –

262
Moq का नवीनतम संस्करण (4.2.1312.1622) के साथ

, आप SetupSequence का उपयोग कर की घटनाओं की एक अनुक्रम सेटअप कर सकते हैं।

_mockClient.SetupSequence(m => m.Connect(It.IsAny<String>(), It.IsAny<int>(), It.IsAny<int>())) 
     .Throws(new SocketException()) 
     .Throws(new SocketException()) 
     .Returns(true) 
     .Throws(new SocketException()) 
     .Returns(true); 

कनेक्ट केवल तीसरे और पांचवें प्रयास अन्यथा एक अपवाद फेंक दिया जाएगा पर सफल हो जाएगा कॉलिंग: यहाँ एक उदाहरण है।

repository.SetupSequence(x => x.GetPageByUrl<IPageModel>(virtualUrl)) 
.Returns(null) 
.Returns(pageModel.Object); 
+1

अच्छा जवाब, केवल सीमा "संरक्षितता" संरक्षित सदस्यों के साथ काम नहीं करती है। – Chasefornone

+1

हां, 'SetupSequence() 'कॉलबैक()' के साथ काम नहीं करता है। अगर ऐसा होता है, तो कोई भी "राज्य मशीन" फैशन में मॉक किए गए विधि को कॉल सत्यापित कर सकता है। – urig

1

यहाँ थोड़ा अलग आवश्यकता के साथ समस्या का एक ही प्रकार के लिए पहुँच गया:

तो अपने उदाहरण के लिए यह सिर्फ तरह कुछ होगा।
मैं सम्मान करता विभिन्न इनपुट में आधारित नकली से अलग वापसी मूल्यों और पाया समाधान जो IMO अधिक पठनीय के रूप में यह Moq के कथात्मक वाक्यविन्यास (Mocks को LINQ) का उपयोग करता है पाने के लिए की जरूरत है।

public interface IDataAccess 
{ 
    DbValue GetFromDb(int accountId); 
} 

var dataAccessMock = Mock.Of<IDataAccess> 
(da => da.GetFromDb(It.Is<int>(acctId => acctId == 0)) == new Account { AccountStatus = AccountStatus.None } 
&& da.GetFromDb(It.Is<int>(acctId => acctId == 1)) == new DbValue { AccountStatus = AccountStatus.InActive } 
&& da.GetFromDb(It.Is<int>(acctId => acctId == 2)) == new DbValue { AccountStatus = AccountStatus.Deleted }); 

var result1 = dataAccessMock.GetFromDb(0); // returns DbValue of "None" AccountStatus 
var result2 = dataAccessMock.GetFromDb(1); // returns DbValue of "InActive" AccountStatus 
var result3 = dataAccessMock.GetFromDb(2); // returns DbValue of "Deleted" AccountStatus 
0

accepted answer, साथ ही SetupSequence answer, स्थिरांक लौटने संभालती है।

Returns() में कुछ उपयोगी अधिभार हैं जहां आप मॉक किए गए विधि को भेजे गए पैरामीटर के आधार पर एक मान वापस कर सकते हैं। स्वीकृत उत्तर में दिए गए the solution के आधार पर, उन अधिभारों के लिए यहां एक और विस्तार विधि है।

public static class MoqExtensions 
{ 
    public static IReturnsResult<TMock> ReturnsInOrder<TMock, TResult, T1>(this ISetup<TMock, TResult> setup, params Func<T1, TResult>[] valueFunctions) 
     where TMock : class 
    { 
     var queue = new Queue<Func<T1, TResult>>(valueFunctions); 
     return setup.Returns<T1>(arg => queue.Dequeue()(arg)); 
    } 
} 

दुर्भाग्यवश, विधि का उपयोग करने के लिए आपको कुछ टेम्पलेट पैरामीटर निर्दिष्ट करने की आवश्यकता है, लेकिन परिणाम अभी भी काफी पठनीय है।

repository 
    .Setup(x => x.GetPageByUrl<IPageModel>(path)) 
    .ReturnsInOrder(new Func<string, IPageModel>[] 
     { 
      p => null, // Here, the return value can depend on the path parameter 
      p => pageModel.Object, 
     }); 

से अधिक पैरामीटर (T2, T3, आदि) यदि आवश्यक हो तो साथ विस्तार विधि के लिए भार के बनाएँ।