[英]Expected invocation on the mock once, but was 0 times, with Func(T, TResult)
我似乎對Mock.Verify遇到問題,認為沒有調用方法,但我可以完全驗證它是否存在。
單元測試:
[Test]
public void IterateFiles_Called()
{
Mock<IFileService> mock = new Mock<IFileService>();
var flex = new Runner(mock.Object);
List<ProcessOutput> outputs;
mock.Verify(x => x.IterateFiles(It.IsAny<IEnumerable<string>>(),
It.IsAny<Func<string, ICsvConversionProcessParameter, ProcessOutput>>(),
It.IsAny<ICsvConversionProcessParameter>(),
It.IsAny<FileIterationErrorAction>(),
out outputs), Times.Once);
}
替代單元測試:(在下面的評論之后)
[Test]
public void IterateFiles_Called()
{
Mock<IFileService> mock = new Mock<IFileService>();
var flex = new Runner(mock.Object);
List<ProcessOutput> outputs;
mock.Verify(x => x.IterateFiles(It.IsAny<string[]>(),
flex.ProcessFile, //Still fails
It.IsAny<ICsvConversionProcessParameter>(),
It.IsAny<FileIterationErrorAction>(),
out outputs), Times.Once);
}
Runner.cs:
public class Runner
{
public Runner(IFileService service)
{
string[] paths = new[] {"path1"};
List<ProcessOutput> output = new List<ProcessOutput>();
service.IterateFiles(paths, ProcessFile, new CsvParam(), FileIterationErrorAction.ContinueThenThrow, out output);
}
public ProcessOutput ProcessFile(string file, ICsvConversionProcessParameter parameters)
{
return new ProcessOutput();
}
}
當我調試時,可以看到該service.IterateFiles
被調用。 另外,因為所有參數都標有It.IsAny<T>
所以傳遞的參數無關緊要(out參數除外-我的理解是這不能被嘲笑)。 但是Moq反對這種方法被稱為。
有什么想法我要去哪里嗎?
基本上,問題在於, Verify
中的某些內容與運行時的內容不完全匹配(這可能非常善變)。
通過將Runner
的代碼更改為:
service.IterateFiles<ICsvConversionProcessParameter, ProcessOutput>(paths, ProcessFile, new CsvParam(), FileIterationErrorAction.ContinueThenThrow, out output);
(顯式指定TFileFunctionParameter
和TFileFunctionOutput
)
這似乎有助於確定moq驗證匹配的類型。
正如@Lukazoid比我說的要好得多,“ Moq將DoSomething視為與DoSomething不同的方法。”
由於排除了一些候選人:
Func<string, ICsvConversionProcessParameter, ProcessOutput>
和ProcessFile
之間似乎不匹配Func<string, ICsvConversionProcessParameter, ProcessOutput>
因為ProcessFile
似乎沒有定義為func。
我可以看到的另一個潛在差異是string[]
與IEnumerable<string>
。
List<ProcessOutput>
作為out參數
NikolaiDante的答案以及其下的評論基本上給出了解釋。 盡管如此,由於我已經對其進行了一些研究,因此我將嘗試將其寫清楚。
您的問題完全無法顯示問題的主要原因,即該方法是通用方法。 我們必須轉到您鏈接的Git文件中進行查找。
IFileService
聲明的方法是:
void IterateFiles<TFileFunctionParameter, TFileFunctionOutput>(
IEnumerable<string> filePaths,
Func<string, TFileFunctionParameter, TFileFunctionOutput> fileFunction,
TFileFunctionParameter fileFunctionParameter,
FileIterationErrorAction errorAction,
out List<TFileFunctionOutput> outputs);
要調用它,就必須同時指定兩個類型參數, TFileFunctionParameter
和TFileFunctionOutput
, 和五個普通參數filePaths
, fileFunction
, fileFunctionParameter
, errorAction
和outputs
。
C#很有幫助,並提供了類型推斷功能,我們不必在源代碼中編寫類型實參。 編譯器會計算出所需類型的參數。 但是兩個類型參數仍然存在,只是“不可見”。 要查看它們,請將鼠標懸停在下面的泛型方法調用上(Visual Studio IDE將向您顯示它們),或者查看輸出IL。
因此,在Runner
類中,該調用實際上意味着:
service.IterateFiles<CsvParam, ProcessOutput>(paths,
(Func<string, CsvParam, ProcessOutput>)ProcessFile,
new CsvParam(), FileIterationErrorAction.ContinueThenThrow, out output);
注意兩個兩種類型的第一行,並注意方法組ProcessFile
實際上變成了Func<string, CsvParam, ProcessOutput>
即使方法簽名看起來更像是Func<string, ICsvConversionProcessParameter, ProcessOutput>
可以從類似的方法組中創建委托。 (與Func<in T1, in T2, out TResult>
在T2
被標記為反變量並沒有什么關系。)
如果我們檢查您的Verify
,那么我們會看到類型推斷確實將其視為:
mock.Verify(x => x.IterateFiles<ICsvConversionProcessParameter, ProcessOutput>(
It.IsAny<IEnumerable<string>>(),
It.IsAny<Func<string, ICsvConversionProcessParameter, ProcessOutput>>(),
It.IsAny<ICsvConversionProcessParameter>(),
It.IsAny<FileIterationErrorAction>(),
out outputs), Times.Once);
因此Moq不能真正驗證是否已調用此方法,因為該調用使用了不同的第一類型參數,並且fileFunction
Func<,,>
也具有另一種類型。 所以這種解釋你的問題。
NikolaiDante顯示如何更改runner
實際使用的類型參數,您的Verify
預期。
但感覺比較合適的兩個轉變的考驗 ,保持runner
代碼不變。 所以我們在測試中想要的是:
mock.Verify(x => x.IterateFiles(It.IsAny<IEnumerable<string>>(),
It.IsAny<Func<string, CsvParam, ProcessOutput>>(),
It.IsAny<CsvParam>(),
It.IsAny<FileIterationErrorAction>(),
out outputs), Times.Once);
(類型推斷將從中給出正確的TFileFunctionParameter
和TFileFunctionOutput
)。
但是:您已經將測試類放在了Runner
類之外的另一個項目/程序集中。 而且CsvParam
類型是其程序集的internal
。 因此,您確實需要使CsvParam
可以訪問我的解決方案中的測試。
您可以通過將類設置為public
,或者通過包括以下屬性,使測試程序集成為MoqIssue程序集的“朋友程序集”,來使CsvParam
可以訪問:
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("MoqIssueTest")]
在屬於MoqIssue項目的某些文件中。
請注意,Moq框架的internal
類型沒有問題,因此您不必為此將Moq的任何程序集轉換為“朋友”。 只需要在MoqIssueTest程序集中輕松地表達“ Verify
(即沒有難看的反射)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.