C# NSubstitute Async返回null,尽管定义了返回对象

C# NSubstitute Async返回null,尽管定义了返回对象,c#,unit-testing,asynchronous,C#,Unit Testing,Asynchronous,我有一个单元测试,它应该返回指定的对象,但它返回null 要测试的数据提供程序: public class PlanDataProvider : BaseDomainServiceProvider, IPlanDataProvider { //CTOR public PlanDataProvider(IDataAccessTemplate template, IEntityStore entityStore) : base(template, entityStore) {

我有一个单元测试,它应该返回指定的对象,但它返回null

要测试的数据提供程序:

public class PlanDataProvider : BaseDomainServiceProvider, IPlanDataProvider
{
    //CTOR
    public PlanDataProvider(IDataAccessTemplate template, IEntityStore entityStore) : base(template, entityStore)
    {
    }

    public async Task<DefaultActionPlan> GetDefaultActionPlan(string referenceListId)
    {
        var objectId = GetObjectId(referenceListId);

        var defaultActionPlan = await Template.InvokeAsync(context => Task.FromResult(EntityStore.GetEntityById<DefaultActionPlan, ObjectId>
            (
                context.ActivityContext as IDataAccessContext,
                typeof(DefaultActionPlan).FullName,
                objectId
            )));
    }
}
公共类PlanDataProvider:BaseDomainServiceProvider、IPlanDataProvider
{
//执行器
public PlanDataProvider(IDataAccessTemplate模板,entityStore entityStore):基本(模板,entityStore)
{
}
公共异步任务GetDefaultActionPlan(字符串referenceListId)
{
var objectId=GetObjectId(referenceListId);
var defaultActionPlan=wait Template.InvokeAsync(上下文=>Task.FromResult(EntityStore.GetEntityById
(
context.ActivityContext作为IDataAccessContext,
typeof(DefaultActionPlan).FullName,
目标
)));
}
}
测试:

public async Task GetPlan_BadPlanID()
{
   //Arrange
   string badPlanId = "57509afbc6b48d3f33b2dfcd";

   ...snip...

   DefaultActionPlan jj = new ObjectId(badPlanId);

   //create EntityStore object
   var dataxs = Substitute.For<IDataAccessContext>();
   var estore = Substitute.For<IEntityStore>();
   estore.GetEntityById<DefaultActionPlan, ObjectId>(
        dataxs, 
        typeof(DefaultActionPlan).FullName, 
        new ObjectId(badPlanId))
   .Returns(Task.FromResult(jj).Result);

   var dataAccessTemplate = Substitute.For<IDataAccessTemplate>();

   PlanDataProvider pdp = new PlanDataProvider(dataAccessTemplate, estore);

   //Act
   var t = await pdp.GetDefaultActionPlan(badPlanId);
   //Now this confuses me as the compiler thinks t is DefaultActionPlan NOT Task<DefaultActionPlan>???
}
public异步任务GetPlan\u BadPlanID()
{
//安排
字符串badPlanId=“57509afbc6b48d3f33b2dfcd”;
剪
DefaultActionPlan jj=新对象ID(badPlanId);
//创建EntityStore对象
var dataxs=Substitute.For();
var estore=替换为();
estore.GetEntityById(
dataxs,
typeof(DefaultActionPlan).FullName,
新对象ID(badPlanId))
.Returns(Task.FromResult(jj.Result));
var dataAccessTemplate=Substitute.For();
PlanDataProvider pdp=新的PlanDataProvider(dataAccessTemplate,estore);
//表演
var t=等待pdp.GetDefaultActionPlan(badPlanId);
//现在这让我很困惑,因为编译器认为t是DefaultActionPlan而不是Task???
}
反正t在任何时候都返回null eferytime,调试测试t为null,因为GetDefaultActionPlan没有返回jj,而是返回null

我错过了什么让jj回来


编辑:

Eris和Gabe都正确地指出,我对IEntityStore的模拟不足以作为该值……即使它指定了返回,也不会传递给包装的InvokeAsync,因此我也需要模拟InvokeAsync

Gabe的回答有点像通过Arg。Any不能满足InvokeAsync所需的参数。然而,我并没有为此责备他,因为我花了数小时跟踪多个项目的继承链(这是一家大公司)。他无权做的事

最后,以下是成功的代码:

var estore = Substitute.For<IEntityStore>();
var dataAccessTemplate = Substitute.For<IDataAccessTemplate>();
dataAccessTemplate.InvokeAsync(context => Task.FromResult(
      estore.GetEntityById<DefaultActionPlan>(
               dataxs, typeof(DefaultActionPlan).FullName, new ObjectId(badPlanId))))
.ReturnsForAnyArgs(jj);

var pdp = new PlanDataProvider(dataAccessTemplate, estore);
var estore=Substitute.For();
var dataAccessTemplate=Substitute.For();
dataAccessTemplate.InvokeAsync(上下文=>Task.FromResult(
estore.GetEntityById(
dataxs,typeof(DefaultActionPlan).FullName,新对象ID(badPlanId)))
.ReturnsForayargs(jj);
var pdp=新的PlanDataProvider(dataAccessTemplate,estore);

虽然我在您的代码中看不到这一点,但我将假设GetDefaultActionPlan返回defaultActionPlan变量,并且Template.InvokeAsync是指通过构造函数传入的IDataAccessTemplate

看起来您缺少Template.InvokeAsync的模拟返回值,因为它正在包装另一个调用,所以您只关心它的返回值:

var estore = Substitute.For<IEntityStore>();
var dataAccessTemplate = Substitute.For<IDataAccessTemplate>();
dataAccessTemplate.InvokeAsync(context => Task.FromResult(Arg.Any<DefaultActionPlan>)
    .ReturnsForAnyArgs(jj);

var pdp = new PlanDataProvider(dataAccessTemplate, estore);
var estore=Substitute.For();
var dataAccessTemplate=Substitute.For();
dataAccessTemplate.InvokeAsync(上下文=>Task.FromResult(Arg.Any)
.ReturnsForayargs(jj);
var pdp=新的PlanDataProvider(dataAccessTemplate,estore);

当你创建
IDataAccessTemplate
的替代品时,你从未定义过它的任何方法。Is
Wait Template.InvokeAsync
调用
InvokeAsync
的空模拟,或者实际调用带有参数的方法?我相信你让我走上了正确的道路,但仍在尝试找出模拟的一些细微差别InvokeAsync包装器…似乎与contex=>Task不一样。FromResult…似乎我需要再传递一点。太好了!我花了一些时间创建了简单的代码版本,以了解如何构造InvokeAsync模拟调用和Arg。在我的测试中,任何操作都有效。据我所知,estore.GetEntityById是否返回DefaultActionPla以外的内容反对?