【问题标题】:Moq return type IEnumerable being returned as ListMoq 返回类型 IEnumerable 作为 List 返回
【发布时间】:2019-11-19 20:21:18
【问题描述】:

我无法让 Moq 从存储库方法返回 IEnumerable 类型。

更新 - 1

我希望我只是遗漏了一些东西,但根据要求,这里是完整的实现。

断言objResult.ValueIEnumerable<ListItemDTO> 类型是失败的。

存储库界面

public interface IRepository
{
    Task<IEnumerable<T>> GetListAsync<T>(string storedProcedure, object template);

    Task<IEnumerable<T>> GetListAsync<T>(string sql, CommandType commandType, object template);
}

public interface ICRUDRepository<TEntity>
{
    Task<TEntity> GetByIdAsync(long Id);

    Task<int> AddAsync(TEntity entity);

    Task<int> UpdateAsync(TEntity entity);

    Task<int> DeleteAsync(long id);
}

public interface ITARRepository : IRepository, ICRUDRepository<TARTracker>
{
    Task<IEnumerable<TARTrackerDate>> GetTARTrackerApprovedDates(long tarTrackerId);
    Task<IEnumerable<TARTrackerDate>> MergeTARApprovalDateChanges(DataTable approvedDates);
}

控制器

public class TARTrackerController : Controller
{
    private readonly ITARRepository Repository;
    private readonly ILogger<TARTrackerController> Logger;

    public TARTrackerController(ILogger<TARTrackerController> logger, ITARRepository repository) 
    {
        Repository = repository;
        Logger = logger;
    }

    [HttpGet("TARTrackers")]
    [SwaggerResponse(HttpStatusCode.OK, typeof(IEnumerable<ListItemDTO>))]
    [SwaggerResponse(HttpStatusCode.Unauthorized, null)]
    [SwaggerResponse(HttpStatusCode.InternalServerError, null)]
    [SwaggerResponse(HttpStatusCode.ServiceUnavailable, null)]
    [Description("Get TAR Tracker List")]
    public async Task<IActionResult> Get(long accountId)
    {
        try
        {
            var trackers = await Repository.GetListAsync<ListItemDTO>("spFDBGetAccountTARList", new { @ParamAccountID = accountId });

            return Ok(trackers);
        }
        catch (Exception e)
        {
            Logger.LogError(LogEvents.Services, e, $"An error occured in {0}.{1}", nameof(TARTrackerController), nameof(Get));

            return StatusCode((int)HttpStatusCode.InternalServerError);
        }
    }
}

单元测试:

public class TARTrackerControllerTests
{
    public static ILogger<TARTrackerController> Logger = Mock.Of<ILogger<TARTrackerController>>();
    public static Mock<ITARRepository> Repository = new Mock<ITARRepository>();

    public class GetTARTrackersTests
    {
        [Fct]
        public async Task Returns_OK_With_ListItemDTO()
        {
            //arrange
            var id = 12345;
            IEnumerable<ListItemDTO> expected = new List<ListItemDTO>();

            Repository
                .Setup(repo => repo.GetListAsync<ListItemDTO>(It.IsAny<string>(), It.IsAny<object>()))
                .ReturnsAsync(expected);

            var controller = new TARTrackerController(Logger, Repository.Object);

            //act
            var result = await controller.Get(id);

            //assert
            var objResult = Assert.IsType<OkObjectResult>(result);
            Assert.Equal(typeof(IEnumerable<ListItemDTO>), objResult.Value.GetType());
        }
    }
}

我觉得这应该匹配。似乎无法弄清楚发生了什么。有什么帮助吗?

【问题讨论】:

  • 你试过expected.AsEnumerable()吗?
  • @MuhammadHannan 是的,它仍然返回List&lt;T&gt;
  • @dbarth 实际发生的事情与您期望发生的事情相反。
  • 我怀疑您希望expected.GetType() 返回IEnumerable - 请edit 发帖澄清是这种情况还是现在(以及更新示例代码+ 预期结果为@Nkosi建议)
  • 你真的关心实际值的类型吗?您关心的是返回值可以枚举并包含期望值。

标签: c# unit-testing .net-core moq


【解决方案1】:

因此,似乎只有断言是错误的,这是这里的普遍共识,但实际上应该通过以下方式完成。

Assert.IsAssignableFrom<IEnumerable<ListItemDTO>>(objResult.Value);

【讨论】:

    【解决方案2】:

    在您显示为控制器代码的IEnumerable 中有拼写错误,这表明它不是您真正使用的代码。这可能或可能不意味着错误与测试代码无关。

    我不确定您的代码中的问题出在哪里,但这是一个通过测试。

    public class ListItemDTO { public string Name { get; set; } }
    
    public interface IRepo
    {
        Task<IEnumerable<T>> GetListAsync<T>(string storedProcedure, object template);
    }
    
    public class Ctrl
    {
        private readonly IRepo repo;
        public Ctrl(IRepo repo) { this.repo = repo; }
    
        // BTW. Check out IAsyncEnumerable<T> 
        public async Task<IEnumerable<ListItemDTO>> GetAync()
            => await repo.GetListAsync<ListItemDTO>("spGetAccountList", new { @ParamAccountID = 1 });
    }
    
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public async Task Test1()
        {
            var mockRepo = new Mock<IRepo>(MockBehavior.Strict);
    
            IEnumerable<ListItemDTO> expected = new List<ListItemDTO>() { new ListItemDTO { Name = "Prince" } };
    
            mockRepo.Setup(repo => repo.GetListAsync<ListItemDTO>(It.IsAny<string>(), It.IsAny<object>()))
                    .ReturnsAsync(expected);
    
            var tested = new Ctrl(repo: mockRepo.Object);
    
            var actual = await tested.GetAync();
    
            Assert.IsNotNull(actual);
            Assert.AreEqual(1, actual.Count(), "Expecting exactly one item");
            var first = actual.First();
            Assert.AreEqual("Prince", first.Name, "Checking 1st item's 'Name'");
        }
    }
    

    库设置如下:

    <package id="Moq" version="4.13.1" targetFramework="net472" />
    <package id="MSTest.TestAdapter" version="2.0.0" targetFramework="net472" />
    <package id="MSTest.TestFramework" version="2.0.0" targetFramework="net472" />
    

    【讨论】:

      【解决方案3】:

      更换

      IEnumerable&lt;ListItemDTO&gt; expected = new List&lt;ListItemDTO&gt;();

      IEnumerable&lt;ListItemDTO&gt; expected = Enumerable.Empty&lt;ListItemDTO&gt;

      应该可以。

      但是,我不确定这是否是个问题;因为List&lt;T&gt;already is an implementationIEnumerable&lt;T&gt;,所以从“打字”的角度来看,还算可以;如果类型是 List&lt;T&gt;IEnumerable&lt;T&gt; 一样,您想要对返回数据的内容等进行的任何检查都可以正常工作

      【讨论】:

      • @tymtam - 因为它返回一个IEnumerable&lt;T&gt;,这将是一个精确的类型匹配。请记住,最初提出的问题并没有真正说明问题是什么(没有关于测试中断言失败的详细信息等),所以在黑暗中使用上述答案进行了一点尝试。
      猜你喜欢
      • 2010-09-27
      • 2019-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-09
      • 1970-01-01
      相关资源
      最近更新 更多