xUnit 和 .Net 核心 post automapper 测试用例问题(我猜)

xUnit and .Net core post test case issue with automapper(I guess)

我正在处理具有配置 .Net 5、Automapper、xUnit 等的 CRUD 单元测试用例

问题:

失败的测试用例:

[Fact]
public void PostTest()
{
    try
    {
        //Arrange
        var surveyRequest = new SurveyRequest()
        {
            Id = 0,
            Name = "Survey Request 1",
            CreateDate = DateTime.Now,
            CreatedBy = 1
        };
        var addedSurveyRequest = new SurveyRequest()
        {
            Id = 1,
            Name = "Survey Request 1",
            CreateDate = DateTime.Now,
            CreatedBy = 1
        };

        //setup mock
        Mock<IRepositoryWrapper> mockRepo = new Mock<IRepositoryWrapper>();
        mockRepo.Setup(m => m.SurveyRequest.Add(addedSurveyRequest)).Returns(new Response<SurveyRequest>(true, addedSurveyRequest));

        //auto mapper
        var mockMapper = new MapperConfiguration(cfg =>
        {
            cfg.AddProfile(new AutoMapperProfile());
        });
        var mapper = mockMapper.CreateMapper();

        SurveyRequestController controller = new SurveyRequestController(repositories: mockRepo.Object, mapper: mapper);

        //Act
        var model = mapper.Map<SurveyRequest, SurveyRequestDto>(source: addedSurveyRequest);
        var result = controller.Post(model); // The issue with this post call here is that response remains null on repository level.

        //Assert
        var okResult = result as OkObjectResult;
        Assert.NotNull(okResult);

        //we will make sure that returned object is dto and not actual entity
        var response = okResult.Value as SurveyRequestDtoWithId;
        Assert.NotNull(response);

        Assert.Equal(expected: response.Name, actual: model.Name);
    }
    catch (Exception ex)
    {
        //Assert                
        Assert.False(true, ex.Message);
    }
}

控制器端post调用:

[HttpPost("Insert")]
public IActionResult Post([FromBody] SurveyRequestDto model)
{
    try
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        //If I remove this mapping from here, test case will work. (see next working test case)
        var entity = _mapper.Map<SurveyRequestDto, SurveyRequest>(source: model);
        entity.CreateDate = System.DateTime.Now;
        entity.CreatedBy = 1;

        var response = _repositories.SurveyRequest.Add(entity: entity); //Response remains null here
        _repositories.Save();

        if (response.IsSuccess == true)
            return new OkObjectResult(_mapper.Map<SurveyRequest, SurveyRequestDtoWithId>(source: response.Data));
        else
            return new ObjectResult(response.ErrorMessage) { StatusCode = 500 };
    }
    catch (Exception ex)
    {
        return new ObjectResult(ex.Message) { StatusCode = 500 };
    }            
}

工作测试用例:

[Fact]
public void PostTest2()
{
    try
    {
        //Arrange
        var surveyRequest = new SurveyRequest()
        {
            Id = 0,
            Name = "Survey Request 1",
            CreateDate = DateTime.Now,
            CreatedBy = 1
        };
        var addedSurveyRequest = new SurveyRequest()
        {
            Id = 1,
            Name = "Survey Request 1",
            CreateDate = DateTime.Now,
            CreatedBy = 1
        };

        //setup mock
        Mock<IRepositoryWrapper> mockRepo = new Mock<IRepositoryWrapper>();
        mockRepo.Setup(m => m.SurveyRequest.Add(surveyRequest)).Returns(value: new Response<SurveyRequest>(true, addedSurveyRequest));

        //auto mapper
        var mockMapper = new MapperConfiguration(cfg =>
        {
            cfg.AddProfile(new AutoMapperProfile());
        });
        var mapper = mockMapper.CreateMapper();

        //setup controlller
        SurveyRequestController controller = new SurveyRequestController(repositories: mockRepo.Object, mapper: mapper);

        //Act
        //var model = mapper.Map<SurveyRequest, SurveyRequestDto>(source: surveyRequest);
        var result = controller.Post2(entity: surveyRequest);

        //Assert
        var okResult = result as OkObjectResult;
        Assert.NotNull(okResult);

        ///we will make sure that returned object is dto and not actual entity
        var response = okResult.Value as SurveyRequestDtoWithId;
        Assert.NotNull(response);

        Assert.Equal(expected: response.Id, actual: addedSurveyRequest.Id);
        Assert.Equal(expected: response.Name, actual: addedSurveyRequest.Name);
    }
    catch (Exception ex)
    {
        //Assert                
        Assert.False(true, ex.Message);
    }
}

控制器端Post调用工作测试用例:

[HttpPost("Insert")]
public IActionResult Post2([FromBody] SurveyRequest entity)
{
    try
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        //var entity = _mapper.Map<SurveyRequestDto, SurveyRequest>(source: model);
        //entity.CreateDate = System.DateTime.Now;
        //entity.CreatedBy = 1;

        var response = _repositories.SurveyRequest.Add(entity: entity); //This returns proper response with saved data and ID
        _repositories.Save();

        if (response.IsSuccess == true)
            return new OkObjectResult(_mapper.Map<SurveyRequest, SurveyRequestDtoWithId>(source: response.Data));
        else
            return new ObjectResult(response.ErrorMessage) { StatusCode = 500 };
    }
    catch (Exception ex)
    {
        return new ObjectResult(ex.Message) { StatusCode = 500 };
    }
}

我不确定我的映射器测试用例设置是否有误或其他问题。我也尝试了很多方法,但到目前为止还没有运气。所以 post 如果有人可以查看并提供帮助,我将不胜感激。

如果您使用的是 IMapper,那么您可以这样做:

var mapperMock = new Mock<IMapper>();
mapperMock
   .Setup(mapper => mapper.Map<SurveyRequestDto, SurveyRequest>(It.IsAny< SurveyRequestDto>()))
   .Returns(surveyRequest);

此解决方案不使用 AutoMapperProfile,但因为您只有一个映射,所以我认为这不是真正的问题。

如果你想在 mapperMock 上调用 Verify 那么我建议像这样提取 Map 选择器委托:

private static Expression<Func<IMapper, SurveyRequestDto>> MapServiceModelFromRequestModelIsAny =>
   mapper => mapper.Map<SurveyRequestDto, SurveyRequest>(It.IsAny< SurveyRequestDto>());

用法

//Arrange
mapperMock
   .Setup(MapServiceModelFromRequestModelIsAny)
   .Returns(surveyRequest);

...

//Assert
mapperMock
   .Verify(MapServiceModelFromRequestModelIsAny, Times.Once);

更新#1

当你做出断言时,尽可能明确也是有意义的。如果您愿意,可以进行深度相等性检查以确保控制器的参数在 Map 调用之前未被修改:

private static Expression<Func<IMapper, SurveyRequestDto>> MapServiceModelFromRequestModel(SurveyRequestDto input) =>
   mapper => mapper.Map<SurveyRequestDto, SurveyRequest>(It.Is< SurveyRequestDto>(dto => dto.deepEquals(input)));


//Assert
mapperMock
   .Verify(MapServiceModelFromRequestModel(model), Times.Once);

这假定 deepEquals 可用作扩展方法。


更新#2

事实证明,模拟存储库的 Setup 代码也有一些问题。即它使用 surveyRequest 而不是 It.IsAny<SurveyRequest>().

因为 surveyRequest 被指定为预期参数,这就是为什么永远不会调用设置的代码路径,而是返回 null

将其更改为 It.IsAny 之后整个测试开始工作 :D

repoMock
   .Setup(repo => repo.SurveyRequest.Add(It.IsAny<SurveyRequest>()))
   .Returns(new Response<SurveyRequest>(true, addedSurveyRequest))