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

GPG*_*GVM 5 c# unit-testing asynchronous

我有一个单元测试应该返回指定的对象,但它返回 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
            )));
    }
}
Run Code Online (Sandbox Code Playgroud)

考试:

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>???
}
Run Code Online (Sandbox Code Playgroud)

无论如何 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);
Run Code Online (Sandbox Code Playgroud)

小智 5

虽然我在您的代码中没有看到这一点,但我假设 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);
Run Code Online (Sandbox Code Playgroud)