You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core单元测试:如何执行Expression<Action<T>>传入的方法?

解决方法

你的Stub无法正常调用目标方法的核心原因是:Expression<Action<T>>描述的是必须依赖T类型实例才能执行的实例方法,而你之前的代码没有提供对应的T实例就直接尝试调用,导致执行失败。

修正后的Stub实现

方案一:基于依赖注入获取实例(推荐用于常规单元测试场景)

如果你的单元测试使用了依赖注入框架(比如结合xUnit使用Microsoft.Extensions.DependencyInjection),可以让Stub通过IServiceProvider获取目标服务实例:

public class BackgroundTasksServiceStub : IBackgroundTasksService
{
    private readonly IServiceProvider _serviceProvider;

    public BackgroundTasksServiceStub(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void RunBackgroundTask<T>(Expression<Action<T>> methodCall)
    {
        // 从DI容器中获取T类型的服务实例
        var serviceInstance = _serviceProvider.GetRequiredService<T>();
        // 编译表达式为可执行委托,传入实例执行方法
        var executeAction = methodCall.Compile();
        executeAction(serviceInstance);
    }
}

方案二:手动指定实例(适用于简单测试场景)

如果测试场景不需要完整的DI容器,也可以在Stub中添加属性手动传入目标实例:

public class BackgroundTasksServiceStub : IBackgroundTasksService
{
    // 用于手动设置要调用的服务实例
    public object? TargetInstance { get; set; }

    public void RunBackgroundTask<T>(Expression<Action<T>> methodCall)
    {
        if (TargetInstance is not T serviceInstance)
        {
            throw new InvalidOperationException($"请设置类型为{typeof(T).FullName}的实例");
        }
        var executeAction = methodCall.Compile();
        executeAction(serviceInstance);
    }
}

测试使用示例

以DI方案为例,测试时的代码示例:

// 构建测试用的服务容器
var services = new ServiceCollection();
services.AddScoped<MyService>(); // 注册你的业务服务
services.AddScoped<IBackgroundTasksService>(sp => new BackgroundTasksServiceStub(sp));

using var serviceProvider = services.BuildServiceProvider();

// 获取后台任务服务并调用
var backgroundTaskService = serviceProvider.GetRequiredService<IBackgroundTasksService>();
backgroundTaskService.RunBackgroundTask<MyService>(x => x.Execute(1, "test_item", "blue"));
// 此时会直接同步执行MyService的Execute方法

原代码失败的原因

你之前的代码Expression.Lambda(methodCall).Compile().DynamicInvoke();存在两个关键问题:

  1. methodCall本身已经是Expression<Action<T>>类型,无需再用Expression.Lambda包装,直接调用Compile()即可得到Action<T>委托。
  2. Action<T>要求传入一个T类型的参数(即实例对象),但你调用DynamicInvoke()时没有传递任何参数,因此无法执行实例方法。

内容的提问来源于stack exchange,提问作者The Fabio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 18:22:45