简体   繁体   中英

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

I try to write a base unit-test class for testing my api-controller. However, I seem to have a problem with IDisposable, because I get the following error:

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-method to be tested:

[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 with test method:

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);
    }
}

I found a solution by using 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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM