我的测试用例存在问题,我试图模仿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);
}
我的测试用例有什么问题?
答案 0 :(得分:1)
模拟方法的参数的配置期望与传递给它的内容不匹配,因此它将返回null。
您目前期望default(Func<IEnumerable<int>>)
默认为null
,但在执行模拟时,您会传递一个与配置的期望值不匹配的实际函数。
使用Arg.Any
作为第二个参数,使模拟期望在行使时更加灵活。
cacheProvider
.Fetch(Arg.Any<string>(), Arg.Any<Func<IEnumerable<int>>>())
.ReturnsForAnyArgs(GetFakeCacheDB());
答案 1 :(得分:1)
测试失败的原因是因为存根调用与实际运行的调用不同。由于泛型参数,这很难看到,但是如果你明确指定它们,你会得到这样的东西:
// 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
。有关详细说明,请参阅this post。
要使测试按预期工作,请确保通用调用签名匹配,方法是显式指定泛型,或确保传递的参数产生正确的泛型。
// 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>();
}