C#xUnit测试实际返回的值为空。

4

我试图使用 xUnit 编写单元测试,针对的是使用 DapperSqllite 数据库获取数据的 EntityService 类。在我的单元测试中,它总是返回实际值作为 List<Entity> [],导致测试失败。我不明白我漏掉了什么。

测试类

public class EntityServiceTests
{
    private readonly IEntityService _sut;
    private readonly Mock<IEntityService> _entityServiceMock;

    public EntityServiceTests()
    {
        _entityServiceMock = new Mock<IEntityService>();

        var dapperMock = new Mock<IDapperWrapper>();
        _sut = new EntityService(dapperMock.Object);
    }

    [Fact]
    public void GetEntities_ShouldReturnEntities()
    {
        //Arrange
        var expectedEntities = new List<Entity>
        {
            new()
            {
                Id = 1,
                Name = "Test",
                X = 12,
                Y = 14.5,
                CoordId = 1
            },
            new()
            {
                Id = 2,
                Name = "Test2",
                X = 16,
                Y = 18.5,
                CoordId = 2
            }
        };

        var query = @"SELECT ent.*, crd.x, crd.y, crd.id as CoordId 
                            FROM entities ent 
                            INNER JOIN coords crd ON crd.entityId = ent.Id";

        _entityServiceMock.Setup(x => x.GetEntities(0)).Returns(expectedEntities);

        //Act
        var entities = _sut.GetEntities(0).ToList();

        //Assert

        Assert.Equal(expectedEntities, entities);
    }
}  

服务类

public class EntityService : IEntityService
{
    private readonly IDapperWrapper _dapperWrapper;

    public EntityService(IDapperWrapper dapperWrapper)
    {
        _dapperWrapper = dapperWrapper;
    }

    public IEnumerable<Entity> GetEntities(int entityId)
    {
        var query = @"SELECT ent.*, crd.x, crd.y, crd.id as CoordId 
                            FROM entities ent 
                            INNER JOIN coords crd ON crd.entityId = ent.Id ";
        if (entityId > 0)
        {
            query += " WHERE ent.id = @id";

            var entities = _dapperWrapper.Query<Entity>(query, new { id = entityId });
            return entities;
        }
        else
        {
            var entities = _dapperWrapper.Query<Entity>(query);

            return entities;
        }
    }
}  

Dapper包装类

public class DapperWrapper : IDapperWrapper
{
    private IDbConnection Connection { get; }

    public DapperWrapper()
    {
        Connection = new SQLiteConnection(GetConnectionString("ConnectionString"));
    }

    private string GetConnectionString(string name)
    {
        return ConfigurationManager.ConnectionStrings[name].ConnectionString;
    }

    public IEnumerable<T> Query<T>(string query)
    {
        return Connection.Query<T>(query);
    }

    public IEnumerable<T> Query<T>(string query, object param)
    {
        return Connection.Query<T>(query, param);
    }

    public T QuerySingle<T>(string query, object param)
    {
        return Connection.QuerySingle<T>(query, param);
    }
}  

单元测试结果

通常项目运行良好,但在测试中失败。我认为我缺少某些基础但至关重要的东西。

更新:我现在使用了这段代码,但仍然收到失败的结果。

    [Fact]
    public void GetEntities_Should_Return_All_Entities_If_0_Is_Provided_In_Param()
    {
        //Arrange
        var expectedEntities = new List<Entity>
        {
            new()
            {
                Id = 1,
                Name = "Test",
                X = 12,
                Y = 14.5,
                CoordId = 1
            },
            new()
            {
                Id = 2,
                Name = "Test2",
                X = 16,
                Y = 18.5,
                CoordId = 2
            }
        };

        var query = @"SELECT ent.*, crd.x, crd.y, crd.id as CoordId 
                        FROM entities ent 
                        INNER JOIN coords crd ON crd.entityId = ent.Id ";

        var dapperMock = new Mock<IDapperWrapper>();
        dapperMock.Setup(x => x.Query<Entity>(query)).Returns(expectedEntities);
        var sut = new EntityService(dapperMock.Object);

        //Act
        var entities = sut.GetEntities(0);

        //Assert
        Assert.Equal(expectedEntities, entities);
    }

你没有使用_entityServiceMock,那么为什么要设置它。而且你在_sut上调用GetEntities,它并没有以任何方式使用你期望的实体。我不明白你为什么期望它能够工作。 - Palle Due
@PalleDue _entityServiceMock.Setup(x => x.GetEntities(0)).Returns(expectedEntities); 我在这里使用它来设置模拟和期望值。 - Rao Arman
@PalleDue 我已经更新了代码,但仍然出现相同的错误。请检查一下。 - Rao Arman
我猜你的设置应该是:dapperMock.Setup(x => x.Query<Entity>(It.IsAny<string>())).Returns(expectedEntities);。据我理解,Moq只有在使用完全相同的字符串引用查询时才会返回。但是你的测试仍然没有太大价值。 - Palle Due
1
问题不在于模拟,当我调试时它是有效的。问题出在这一行 var entities = sut.GetEntities(0); 当我调试时,我看到一切都运行良好,但是当查询执行时,它返回了空值。我无法理解为什么... - Rao Arman
显示剩余4条评论
1个回答

2
我认为我缺少了某些非常基本但至关重要的东西。
确实,mocking旨在用于模拟从单元测试(sut)到依赖项的依赖关系。 在您的情况下,要测试的单元是EntityService,要模拟掉的依赖项是IDapperWrapper。
但是,您不仅要模拟依赖项,还要使用_entityServiceMock来模拟SUT。
因此,您可以尝试在您的测试中这样做:
public class EntityServiceTests
{
    [Fact]
    public void GetEntities_ShouldReturnEntities()
    {
        //Arrange
        var expectedEntities = new List<Entity>
        {
            new()
            {
                Id = 1,
                Name = "Test",
                X = 12,
                Y = 14.5,
                CoordId = 1
            }
        };

        var query = @"SELECT ent.*, crd.x, crd.y, crd.id as CoordId 
                            FROM entities ent 
                            INNER JOIN coords crd ON crd.entityId = ent.Id WHERE ent.id = @id";
        var dapperMock = new Mock<IDapperWrapper>();
        dapperMock.Setup(x => x.Query<Entity>(query, 1)).Returns(expectedEntities.First());
        var sut = new EntityService(dapperMock.Object);

        //Act
        var entities = sut.GetEntities(1).ToList();

        //Assert
        Assert.Equal(expectedEntities, entities);
    }
}  

但我强烈不建议你这样做,因为这样会嘲笑所有的 Dapper 东西。
相反,最好使用内存数据库(如 SQLite),使用真正的 Dapper 实例。这样,您的测试将变得更加全面、更短且面向业务。 这篇博客文章 应该会给你一个很好的起点。

我仍然得到相同的结果。返回的实体为空,测试失败。我已经更新了问题,麻烦请检查一下。 - Rao Arman
当然可以,因为在你的代码中expectedEntities只包含一个Id=1的对象,但是dapperMock被配置为Id=0 - mu88
1
dapper被配置为返回所有内容。因为如果提供0个id,则不会附加Where子句。无论提供1个id仍然返回相同的结果。问题是它只是不包含任何结果来进行断言。实际结果值为空列表。 - Rao Arman
1
这正是我不鼓励使用这种模式的原因:你会花费很多时间,但几乎没有任何价值。相反,最好使用内存数据库和真正的Dapper实例,这样你会感到满意。 - mu88
目前我没有其他选择,但它就是不起作用,我不知道为什么 :( - Rao Arman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接