微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用“动态”通用和匿名对象“new {}”进行模拟失败

如何解决使用“动态”通用和匿名对象“new {}”进行模拟失败

Visual Studio 2019 企业版 16.9.4;起订量 4.16.1; xunit 2.4.1; net5.0

我正在尝试对我的 AlbumData.GetAlbumsAsync() 方法进行单元测试。我模拟了 sqlDataAccess 层,它在 generic 方法中使用 Dapper 调用数据库

这是我的设置。模拟不起作用。在 AlbumData.GetAlbumsAsync() 方法中,对模拟对象 (_sql.LoadDataAsync) 的调用返回 null,而 output 设置为 null。

谁能告诉我我做错了什么?

sqlDataAccess.cs

public async Task<List<T>> LoadDataAsync<T,U>(string storedProcedure,U parameters,string connectionStringName)
{
  string connectionString = GetConnectionString(connectionStringName);

  using (IDbConnection connection = new sqlConnection(connectionString))
  {
    IEnumerable<T> result = await connection.QueryAsync<T>(storedProcedure,parameters,commandType: CommandType.StoredProcedure);
    List<T> rows = result.ToList();
    return rows;
  }
}

相册数据.cs

public class AlbumData : IAlbumData
{
    private readonly IsqlDataAccess _sql;
    
    public AlbumData(IsqlDataAccess sql)
    {
      _sql = sql;
    }

    public async Task<List<AlbumModel>> GetAlbumsAsync()
    {
      var output = await _sql.LoadDataAsync<AlbumModel,dynamic>
        ("dbo.spAlbum_GetAll",new { },"AlbumConnection");

      return output;
    }
    ...
}
    

专辑数据测试.cs

public class AlbumDataTest
{
    private readonly List<AlbumModel> _albums = new()
    {
      new AlbumModel { Title = "Album1",AlbumId = 1 },new AlbumModel { Title = "Album2",AlbumId = 2 },new AlbumModel { Title = "Album3",AlbumId = 3 }
    };

    [Fact]
    public async Task getAlbums_returns_multiple_records_test()
    {
      Mock<IsqlDataAccess> sqlDataAccessMock = new();
      sqlDataAccessMock.Setup(d => d.LoadDataAsync<AlbumModel,dynamic>
        (It.IsAny<string>(),It.IsAny<string>()))
           .Returns(Task.Fromresult(_albums));

      AlbumData albumData = new AlbumData(sqlDataAccessMock.Object);

      List<AlbumModel> actual = await albumData.GetAlbumsAsync();

      Assert.True(actual.Count == 3);
    }
    ...
}

更新 1:

根据@freeAll 和@brent.reynolds 的建议,我更新了测试以使用 It.IsAny<string>()

还更新了@brent.reynolds fiddle 以实际实现单元测试:

https://dotnetfiddle.net/nquthR

这一切都在小提琴中起作用,但是当我将完全相同的测试粘贴到我的 AlbumDataTest 中时,它仍然返回 null。 Assert.Null(actual); 通过,Assert.True(actual.Count == 3); 失败。

更新 2:

我已向 https://github.com/PerProjBackup/Failing-Mock 发布了一个测试失败的项目。 如果您运行 API.Library.ConsoleTests 项目,则 Mock 可以工作。如果您在 API.Library.Tests 项目中使用 Test Explorer 运行测试,则 Mock 将失败。

@brent.reynolds 能够通过将 dynamic 泛型更改为 object 来使 Mock 工作。现在正在尝试调试 dynamic 问题。

更新 3:

如果我将 AlbumData 类移动到与 AllbumDataTest 类相同的项目中,模拟工作(使用 dynamic)返回包含三个对象的列表。但是当 AlbumData 类在一个单独的项目中时(就像在现实世界中一样)模拟返回 null。

我已更新 https://github.com/PerProjBackup/Failing-Mock 存储库。我删除了控制台应用程序并创建了一个 Failing 和 Passing 文件夹,其中包含两个场景。

为什么将模拟传递给不同项目的类会使模拟失败?

更新 4:

请参阅 brent.reynolds 接受的答案和我在那里的评论。问题是在模拟设置中使用匿名对象。我已经删除了 Failing-Mock 存储库和 dotnetfiddle

解决方法

可能与异步方法有关。根据{{​​3}},您可以这样做

sqlDataAccessMock
  .Setup(d => d.LoadDataAsync<AlbumModel,dynamic>(
    It.IsAny<string>(),new {},It.IsAny<string>())
    .Result)
  .Returns(_albums);

sqlDataAccessMock
  .Setup(d => d.LoadDataAsync<AlbumModel,It.IsAny<string>()))
  .ReturnsAsync(_albums);

编辑: 尝试将 It.IsAny<object>() 添加到设置中:

sqlDataAccessMock
  .Setup(d => d.LoadDataAsync<AlbumModel,object>(
    It.IsAny<string>(),It.IsAny<object>(),It.IsAny<string>()))
  .Returns(Task.FromResult(_albums));

并将 GetAlbumsAsync() 中的类型参数更改为:

var output = await _sql.LoadDataAsync<AlbumModel,object>(
  "dbo.spAlbum_GetAll",new { },"AlbumConnection");

操作说明/摘要:

在模拟设置中使用匿名对象 new {} 是核心问题。当测试类和被测试的类在同一个项目中时它起作用,但当它们在不同的项目中时不起作用,因为它不能被重用。 It.IsAny<dynamic>() 将不起作用,因为编译器禁止在 LINQ 表达式树中使用 dynamic。 brent.reynolds 使用 object 解决了这个问题。

,

使用 It.IsAny<string>() 代替空字符串

sqlDataAccessMock.Setup(d => d.LoadDataAsync<AlbumModel,dynamic>
    (It.IsAny<string>(),It.IsAny<string>())).Returns(Task.FromResult(_albums));

注意:您不能在动态对象上使用 It.IsAny<T>()

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?