使用xunit测试文件上传API

问题描述 投票:0回答:1

我的课程如下:

public class FileUploadRequest
{
    public IFormFile FileUploaded { get; set; }
}

这是我的控制器:

public class MyController : ControllerBase
{
    private readonly IMyService _service;

    public MyController(IMyService service)
    {
        _service = service;
    }

    /// <summary>
    /// Send a csv file to create a new report.
    /// </summary>
    [HttpPost]
    public async Task<IActionResult> CreateReport([FromForm] FileUploadRequest inputCsvFile)
    {
        IdResponse response = await _service.CreateReport(inputCsvFile);
        return Created("", response);
    }
}

这是我的服务:

public class MyService : IMyService
{
    private readonly IMyRepository _repository;

    private readonly IMapper _mapper;

    public MyService(
        IMyRepository repository,
        IMapper mapper)
    {
        _repository = repository;
        _mapper = mapper;
    }
    public async Task<IdResponse> CreateReport(FileUploadRequest inputCsvFile)
    {
        if (inputCsvFile.FileUploaded == null)
        {
            throw new BusinessException(BusinessErrorCode.NO_FILE_UPLOADED);
        }
        if (inputCsvFile.FileUploaded.ContentType != "text/csv")
        {
            throw new BusinessException(BusinessErrorCode.INVALID_FILE_TYPE, inputCsvFile.FileUploaded.ContentType);
        }
        IEnumerable<ReportDetail> reportDetails = ParseDetailFromCsv(inputCsvFile.FileUploaded);
        if(reportDetails == null || !reportDetails.Any())
        {
            throw new BusinessException(BusinessErrorCode.NO_VALID_DATA);
        }
        IEnumerable<ReportDetailDbModel> details = _mapper.Map<IEnumerable<ReportDetailDbModel>>(details);
        string result = await _repository.CreateReport(inputCsvFile.FileUploaded, reportDetails);
        IdResponse response = new()
        {
            Id = result
        };
        return response;
    }
}

目前,我想使用 xunit 使用保存在 Test 项目的 Asset 文件夹中的示例文件进行测试。这是我的测试:

[Theory]
[InlineData("REPORT_DETAILS_TEST.csv")]
public async Task CreateReport_CreateSuccessfully(string fileName)
{
    // Arrange
    // Create a context with useInMemory option
    using var context = new DataContext(TestHelper.CreateMockDbOptions());
    var repository = new MyRepository(context);
    var service = new MyService(repository, _mapper);
    var controller = new MyController(service);

    string exePath = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!;
    string filePath = exePath + $"/Assets/{fileName}";
    var formFile = new FormFile(
        new MemoryStream(),
        0,
        new FileInfo(filePath).Length,
        "ReportTestFile",
        Path.GetFileName(filePath)
    );

    var inputFile = new FileUploadRequest { FileUploaded = formFile };

    // Act
    var result = await controller.CreateReport(inputFile);

    // Assert
    Assert.NotNull(result);

}

但是,我意识到我无法像使用真实 API 发送时一样创建 FileUploadRequest 对象。 (ContentType、ContentDisposition、Header 字段为空)这会导致服务验证失败。有办法解决这个问题吗?

c# unit-testing mocking xunit
1个回答
0
投票

因为您必须测试真实的 HTTP 行为,所以我不会将其称为 unit 测试,而是称为 integration 测试。在集成测试 ASP Core 控制器的情况下,您必须设置所有底层基础设施,仅实例化控制器的实例是不够的。当然,仍然值得对控制器进行单元测试,但您不能依赖特定于基础设施的详细信息,例如 HTTP 标头等。

有关单元测试的信息,请参阅:https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/testing?view=aspnetcore-8.0 以及: https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-8.0 关于集成测试。

我还建议避免在单个服务中处理 HTTP 详细信息和报告创建。正如您所看到的,它使得服务的单元测试变得不可能 - 因为它依赖于类似基础设施的细节。如果它只获取文件内容,则可以进行单元测试。就我个人而言,我认为编写测试的问题通常表明测试主题中没有足够的独立关注点。

© www.soinside.com 2019 - 2024. All rights reserved.