NSubstitute的ReturnsForAnyArgs返回null,但不应该返回

5

在我的测试用例中遇到了问题,我试图模拟返回我的 ICacheProvider 但它总是返回 null

[Fact]
public void Raise_ShoultReturnTrue_IfItsInCache()
{
    var cacheProvider = Substitute.For<ICacheProvider>();
    cacheProvider.Fetch(Arg.Any<string>(), default(Func<IEnumerable<int>>)).ReturnsForAnyArgs(GetFakeCacheDB());

    //I was expecting the var below to contain the data from GetFakeCacheDB method
    var cacheProviderReturn = cacheProvider.Fetch("anything", returnEmpty); 

    //there is more stuff here but doesnt matter for the question    
}

private HashSet<int> returnEmpty()
{
    return new HashSet<int>();
}

private IEnumerable<int> GetFakeCacheDB()
{
    var cacheData = new List<int>()
                    {
                       57352,
                       38752
                    };    
    return cacheData;
}

public interface ICacheProvider
{
    void Add<T>(string key, T item);
    void Add<T>(string key, T item, DateTime? absoluteExpiry, TimeSpan? relativeExpiry);
    T Fetch<T>(string key, Func<T> dataLookup);
    T Fetch<T>(string key, Func<T> dataLookup, DateTime? absoluteExpiry, TimeSpan? relativeExpiry);
    void Remove<T>(string key);
}

我的测试用例有什么问题?
2个回答

3

测试失败的原因是因为存根调用与实际运行的调用不同。由于泛型参数的存在,这很难看出来,但如果您明确指定它们,您将得到类似于以下内容:

// Arrange
cacheProvider.Fetch<IEnumerable<int>>(/* ... */).ReturnsForAnyArgs(GetFakeCacheDB());
// Act
var cacheProviderReturn = cacheProvider.Fetch<HashSet<int>>("anything", returnEmpty); 

.NET认为Fetch<IEnumerable<int>>()Fetch<HashSet<int>>是两个不同的方法,所以第一行已被存根以返回GetFakeCacheDB()用于任何参数,而第二个方法未被配置,并将返回null。有关更多解释,请参见此文章

为使测试按预期工作,请确保通用调用签名匹配,要么通过显式指定泛型,要么确保传递的参数产生正确的泛型。

// Option 1: explicit generic
var cacheProviderReturn = cacheProvider.Fetch<IEnumerable<int>>("anything", returnEmpty);
// where `returnEmpty` is `Func<HashSet<int>>`

// Option 2: keep stubbed call the same, and change `returnEmpty` return type
// from `HashSet<int>` to `IEnumerable<int>` to make sure it results
// in correct generic being called.
private IEnumerable<int> returnEmpty() {
    return new HashSet<int>();
}

有没有办法使这种问题的诊断/调试更容易? https://www.nuget.org/packages/NSubstitute.Analyzers.CSharp/ 能帮助解决吗? - Michael Freidgeim
能否指定类似于 Arg.Any<Func<IEnumerable<ANY-TYPE>>>() 这样的东西? - Michael Freidgeim
1
@MichaelFreidgeim,NSubstitute不支持像那样的泛型参数匹配任何类型。我相信Moq支持它(https://dev59.com/wGIj5IYBdhLWcg3wnmTA#61844709)。 - David Tchepak

2
配置的模拟方法参数期望与传递给它的不匹配,因此它将返回null。
您当前的期望是default(Func<IEnumerable<int>>),这将默认为null,但在执行模拟时,您传递了一个实际函数,该函数不符合配置的期望。
使用Arg.Any作为第二个参数,使模拟期望在执行时更加灵活。
cacheProvider
    .Fetch(Arg.Any<string>(), Arg.Any<Func<IEnumerable<int>>>())
    .ReturnsForAnyArgs(GetFakeCacheDB());

谢谢你的回答,当我将方法“HashSet<int> returnEmpty()”的签名更改为“IEnumerable<int> returnEmpty()”时,它起作用了。 - TiagoM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接