2015-12-15 83 views
2

這個代碼是基於這個答案在這裏:https://stackoverflow.com/a/3135677/360211賦值給Moq讀取的輸出參數的值。怎麼樣?

public interface IService 
{ 
    void DoSomething(out string a); 
} 

[Test] 
public void Test() 
{ 
    var mock = new Mock<IService>(); 
    string expectedValue = "value"; 
    mock.Setup(s => s.DoSomething(out expectedValue)); 

    string actualValue; 
    mock.Object.DoSomething(out actualValue); 
    Assert.AreEqual("value", actualValue); 
} 

分配string expectedValue = "value";我所看到的,並儘可能的ReSharper而言並不遠需要。但刪除它,斷言失敗!

那麼Moq如何讀取out參數中的值?

+0

您所連結的回答說:「我猜,起訂量着眼於‘expectedValue’當你調用設定值並記住它。「這是你的方法只會返回你給它的預期值。如果你不給它一個值,它不會返回這個值。 – Chris

+0

@Chris是另一個值使它失敗。我知道它做了什麼,問題是它是如何做到的。 – weston

+0

當你在未初始化的時候運行'expectedValue'和/或'actualValue'的內容時,你會得到什麼。 – gmiley

回答

0

一種out參數不是需要被初始化,但它必須由時間函數返回分配給。

我不知道mock.Object.DoSomething(out actualValue);裏面發生了什麼,但是使用out參數定義它似乎很奇怪。根據你所報告的行爲來判斷,我會盡管應該是ref。參數ref必須在作爲參數傳遞給函數之前進行初始化。

+0

「然而它可以在函數調用」不,這不會編譯'「CS0269使用未分配的參數'testVar'' – weston

1

的重要線路是這樣的:

mock.Setup(s => s.DoSomething(out expectedValue));

這樣的設置意味着,調用DoSomething將始終把任何價值是expectedValue到實際出變量的方法執行時的任何方法。

也許考慮一個更有用的場景,它不是直接測試mock.Object,而是測試一個注入了IService的類。

public class Foo 
{ 
    private IService service; 
    public Foo(IService service) 
    { 
     this.service = service; 
    } 

    public string GetData() 
    { 
     string outData; 
     service.DoSomething(out outData); 

     return outData != "" ? outData : "There was no data"; 
    } 
} 

[Test] 
public void FooTest_ServiceReturnsEmptyString() 
{ 
    var mock = new Mock<IService>(); 
    // ReSharper disable once RedundantAssignment 
    string expectedValue = ""; 
    mock.Setup(s => s.DoSomething(out expectedValue)); 

    var fooObj = new Foo(mock.Object); 
    var result = fooObj.GetData(); 

    Assert.AreEqual("There was no data", result); 
} 

[Test] 
public void FooTest_ServiceReturnsValue() 
{ 
    var mock = new Mock<IService>(); 
    string expectedValue = "data"; 
    mock.Setup(s => s.DoSomething(out expectedValue)); 

    var fooObj = new Foo(mock.Object); 
    var result = fooObj.GetData(); 

    Assert.AreEqual(expectedValue, result); 
} 

在兩種不同的測試,你可以改變一下模擬回報,你現在可以測試你如何類的行爲從您的服務給予不同的數據。

+0

謝謝,問題不是什麼是有用的測試和什麼不是,只是如何moq是這個例子只是一個非法的例子, – weston

+0

你的'FooTest_ServiceReturnsValue'會有同樣的問題,一個警告值是'expectedValue'沒有被使用,但是你不能在不破壞測試的情況下移除它 – weston

+0

@weston看到我的編輯..第二次測試時,resharper不會標記變量,因爲如果將它包含在'Assert'中,它可以安全地移除。對於第一次測試,您特別需要將值設置爲爲了使測試通過空字符串..你真的可以做的唯一事情就是添加註釋來禁用resharper警告。 – Borophyll

0

我認爲原因是它傳遞的只是一個Expression<Action<IService>>類型的表達式,並且捕獲了表達式中的參數(所以如果您更改了expectedValue的值,表達式中的值也會發生變化)。值得注意的是,如果使用普通參數而不是out參數,表達式是相同的。

我想可能它根本就沒有對這個參數做任何事情。當你設置這個方法時,你在Mock對象中放入了一堆元數據,告訴它你做了什麼,當你在你的模擬對象上調用該方法時,它會使用該元數據來確定返回值是什麼或者可能需要什麼做。

值得注意的是,它可能不會調用任何東西,並將其參數與out修飾符一起傳遞,所以結果基本相同,如果您沒有該行,那就是Mock甚至不會嘗試設置該變量的值。這意味着,基本上你在聲明它時設置expectedValue的值,然後沒有任何改變它,然後你有一個基於它的斷言。

我無法做任何測試來證明這一理論,而且我對錶達式的理解還不夠自信,或者模擬說這絕對是發生了什麼,但從我所見過的代碼我認爲它至少接近於此。我的不確定性是這樣的,我打算把它作爲一個評論發表,但是它有點太長了。;-)

說了這麼多,即使Mock正在做一些事情,儘管它是out參數,它仍然可以讀取你的變量,這是它是表達式的一部分。您應該能夠通過使用下面的代碼,設置適當的斷點和檢查,看看這個:

string expectedValue = "FNORDFNORD"; 
Expression<Action<IService>> expression = s => s.DoSomething(out expectedValue);