Xunit - 如何使用异步任务和反射测试方法

Xunit - How to test a method with async task and reflection

我尝试编写一个基本单元测试 class 来测试我的 api-控制器。但是,我似乎对 IDisposable 有问题,因为我收到以下错误:

Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.

API-待测方法:

[HttpGet("{id}")]
virtual public async Task<IActionResult> GetById(long id)
{
    var type = typeof(TManager);
    ConstructorInfo? constructorInfo = type.GetConstructor(new[] { typeof(ApiDbContext), typeof(IMapper) });
    if (constructorInfo == null)
        return BadRequest();

    object classObject = constructorInfo.Invoke(new object[] { dbContext, mapper });
    MethodInfo? methodInfo = type.GetMethod("GetByIdDto");
    if (methodInfo == null)
        return BadRequest();

    var task = Task.Run(() => methodInfo.Invoke(classObject, new object[] { id }));
    TEntityDTO entity = (TEntityDTO)await task.ConfigureAwait(false);

    if (entity == null)
    {
        return NotFound("Record couldn't be found.");
    }

    return Ok(entity);
}

Class 测试方法:

public abstract class BaseControllerUnitTests<TController, TEntity, TEntityDTO> : IDisposable where TController : class
{
    protected IMapper Mapper { get; set; }
    protected ApiDbContext Context { get; set; }

    protected BaseControllerUnitTests()
    {
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // dispose managed resources
            Context.Dispose();
        }
        // free native resources
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    [Fact]
    public async Task TestGetItemIdNotFound()
    {
        // Arrange
        long id = 99;
        var type = typeof(TController);
        ConstructorInfo constructorInfo = type.GetConstructor(new[] { typeof(ApiDbContext), typeof(IMapper) });
        Assert.NotNull(constructorInfo);

        object classObject = constructorInfo.Invoke(new object[] { Context, Mapper });
        MethodInfo methodInfo = type.GetMethod("GetById");
        Assert.NotNull(methodInfo);

        // Act
        var task = Task.Run(() => methodInfo.Invoke(classObject, new object[] { id }));
        var controllerResponse = await task.ConfigureAwait(false);

        // Assert
        Assert.IsType<NotFoundObjectResult>(controllerResponse);
        var objectResponse = controllerResponse as ObjectResult;
        Assert.Equal(404, objectResponse.StatusCode);
    }
}

我找到了使用 dynamic

的解决方案
[Fact]
public async Task TestGetItemIdNotFound()
{
    // Arrange
    long id = 99;
    var type = typeof(TController);
    ConstructorInfo constructorInfo = type.GetConstructor(new[] { typeof(ApiDbContext), typeof(IMapper) });
    Assert.NotNull(constructorInfo);

    object classObject = constructorInfo.Invoke(new object[] { Context, Mapper });
    MethodInfo methodInfo = type.GetMethod("GetById");
    Assert.NotNull(methodInfo);

    // Act
    dynamic task = methodInfo.Invoke(classObject, new object[] { id });
    var controllerResponse = await task.ConfigureAwait(false);

    // Assert
    Assert.IsType<NotFoundObjectResult>(controllerResponse);
    var objectResponse = controllerResponse as ObjectResult;
    Assert.Equal(404, objectResponse.StatusCode);
}