2013-03-22 42 views
1

我很難提取任何It.Is<T>參數匹配變量。每當我這樣做,測試失敗。爲什麼我的參數匹配器失敗時,我把它提取到一個變量?

這工作:

calculatorMock 
    .Setup(x => x.Produce(It.Is<IEnumerable<Report>>(xx => reports.IsEqualTo(xx)))) 
    .Returns(calculatorInputs); 

然而,這種失敗:

var argumentMatcher = It.Is<IEnumerable<Report>>(xx => reports.IsEqualTo(xx)); 
calculatorMock 
    .Setup(x => x.Produce(argumentMatcher)) 
    .Returns(calculatorInputs); 

IsEqualTo是一個靜態方法返回布爾。

問題是Moq說Produce()被調用時有一個空列表,當我期待它被包含3個項目的列表調用時。在這個例子中,xx表示空列表。我不確定爲什麼我需要與我的Moq驗證一致的參數匹配。

我剛剛發現了以下工作:

Expression<Func<IEnumerable<Report>, bool>> expression = x => reports.IsEqualTo(x); 
calculatorMock 
    .Setup(x => x.Produce(It.Is(expression))) 
    .Returns(calculatorInputs); 

有爲什麼It.Is<T>不能被提取像我上面嘗試做了具體的原因是什麼?

下面是該問題的工作副本:

using System;使用System.Linq.Expressions的 ;使用Moq的 ;使用Xunit的 ;

命名空間MoqArgumentMatcher { 類節目 { 靜態無效的主要(字符串[]參數) { 變種的TestRunner =新的TestRunner();

 testRunner.Passes(); 
     testRunner.Fails(); 

     Console.ReadKey(); 
    } 
} 

public class TestRunner 
{ 
    [Fact] 
    public void Passes() 
    { 
     // Arrange 
     var calculatorMock = new Mock<ICalculator>(); 
     var consumer = new CalculatorConsumer(calculatorMock.Object); 
     var report = new Report {Id = 1}; 

     // Act 
     consumer.Consume(report); 

     // Assert 
     calculatorMock.Verify(x => x.Produce(
      It.Is<Report>(xx => xx.Id == 1)), Times.Once()); 
    } 

    [Fact] 
    public void Passes2() 
    { 
     // Arrange 
     var calculatorMock = new Mock<ICalculator>(); 
     var consumer = new CalculatorConsumer(calculatorMock.Object); 
     var report = new Report { Id = 1 }; 

     // Act 
     consumer.Consume(report); 

     // Assert 
     Expression<Func<Report, bool>> expression = x => x.Id == 1; 
     calculatorMock.Verify(x => x.Produce(It.Is(expression)), Times.Once()); 
    } 

    [Fact] 
    public void Fails() 
    { 
     // Arrange 
     var calculatorMock = new Mock<ICalculator>(); 
     var consumer = new CalculatorConsumer(calculatorMock.Object); 
     var report = new Report {Id = 1}; 

     // Act 
     consumer.Consume(report); 

     // Assert 
     var argumentMatcher = It.Is<Report>(xx => xx.Id == 1); 
     calculatorMock.Verify(x => x.Produce(argumentMatcher), Times.Once()); 
    } 
} 

public class CalculatorConsumer 
{ 
    private readonly ICalculator _calculator; 

    public CalculatorConsumer(ICalculator calculator) 
    { 
     _calculator = calculator; 
    } 

    public void Consume(Report report) 
    { 
     _calculator.Produce(report); 
    } 
} 

public interface ICalculator 
{ 
    void Produce(Report report); 
} 

public class Report 
{ 
    public int Id { get; set; } 
} 

}

+3

「失敗」過於含糊的描述。 *它如何失敗? – 2013-03-22 17:22:18

+0

@JonSkeet:你是對的。我已經添加了我能想到的一切。 – gcso 2013-03-22 17:38:17

+1

你能在一個簡短但完整的程序中重現這一點嗎?這將使測試更容易。我懷疑這與類型推斷有關,但很難肯定地說。 (這對Moq文檔鏈接在項目頁面上不起作用沒有幫助!) – 2013-03-22 17:44:00

回答

1

Passes2Fails測試之間的差異是最容易理解的,至少對我來說,如在失敗測試的情況下,表達鏈休息。

首先要注意的是It.Is簽名:

TValue It.Is<TValue>(Expression<Func<TValue, bool>> match) 

尤其注意,在執行時,它返回的TValue,而不是一個Expression一個實例。接下來要注意的是,Verify的簽名需要Expression(類型ActionFunc),其中一個調用所需的方法。

當Moq的執行Verify方法,它看起來在表達和提取方法調用它被驗證接着被供給的方法的值的表達式的部分被稱爲,在這種情況下,report論點Produce(Report report)。然後它編譯這個小的參數表達式子樹來執行用於調用Produce方法的值來確定它是否匹配。

在通行證和通行證2的情況下,能夠提取Expression<Func<Report, bool>>。編譯器知道它應該將代碼解析到一個表達式中,以便爲調用創建一個表達式樹。

Fails的情況下,在這條線......

var argumentMatcher = It.Is<Report>(xx => xx.Id == 1); 

...編譯器看到到It.Is一個呼叫會盡快代碼運行進行評估。因此它確定var的類型將是TValue(返回類型),而不是任何東西的Expression。因此,在Verify調用中看到argumentMatcher時,它現在是表達樹中的一個葉節點,它是一個簡單的變量。

在運行時,argumentMatcher可能被評估爲null。 Moq看到parameter-expression-sub-tree是一個值而不是Func,它與該值進行比較,爲null,而不是根據需要執行與1的比較。

(這是儘管OP信納對另一個問題的答案回答開放性問題的精神!)

+0

這是一個很棒的答案!非常感謝您提供非常深刻的答案。 – gcso 2013-09-20 17:13:56

相關問題