如何使用Moq对存储库和模拟数据库进行单元测试

时间:2020-04-28 14:22:01

标签: unit-testing asp.net-core moq repository-pattern

我一个人参加一个没有任何单元测试的现有项目。在进行任何重构之前,我的首要目标是覆盖100%的代码。我想避免任何回归。

我已经读过how do I mock sqlconnection or should I refactor the code?,但是您在下面看到的情况却大不相同,原因是我需要做的不仅仅是存根sqlConnection。基本上,我需要模拟数据库。我想知道实现它的最佳方法。

(顺便说一句,我不想​​使用任何ORM,例如Entity)。 在存储库代码下方:

public class HotelRepository : IHotelRepository
{
    private readonly IDbConnection _dbConnection;
    private readonly ILogService _loggerService;

    public HotelRepository(IDbConnection dbConnection, ILogService loggerService)
    {
        _dbConnection = dbConnection;
        _loggerService = loggerService;
    }

    public HotelDo GetByRid(string rid)
    {
        return Find(rid).FirstOrDefault();
    }

    public List<HotelDo> Find(string text)
    {
        try
        {
            _dbConnection.Open();

            var items = new List<HotelDo>();
            using (var command = _dbConnection.CreateCommand())
            {
                command.CommandType = CommandType.StoredProcedure;
                command.CommandText = "dbo.HotelSearchByRidOrName";

                command.Parameters.Add(new SqlParameter("@Text", text));

                using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
                {
                    while (reader.Read())
                    {
                        items.Add(new HotelDo()
                        {
                            Name = SqlExtension.ReaderToStringConverter(reader["Name"]),
                            Id = SqlExtension.ReaderToIntConverter(reader["Id"]),
                            Rid = SqlExtension.ReaderToStringConverter(reader["RIDHotel"]),
                            IdPms = SqlExtension.ReaderToNullableIntConverter(reader["IdPms"]),
                            LinkResaWeb = SqlExtension.ReaderToStringConverter(reader["LinkResaWeb"]),
                            LinkPms = SqlExtension.ReaderToStringConverter(reader["LinkPms"]),
                            IdBrand = SqlExtension.ReaderToNullableIntConverter(reader["IdBrand"]) ?? 0,
                            IsOnline = SqlExtension.ReaderToBoolConverter(reader["IsOnline"]) ?? false,
                            CodeCountry = SqlExtension.ReaderToStringConverter(reader["CodeCountry"])
                        });
                    }
                }
            }
            return items;
        }
        catch (Exception e)
        {
            var errorMessage = $"HotelRepository Find, text {text} ";
            _loggerService.Trace(LogSeverity.Error, errorMessage, e);

            throw new DalException() { Source = errorMessage, };
        }
        finally
        {
            _dbConnection.Close();
        }
    }

    public List<HotelDo> GetAll()
    {
        try
        {
            _dbConnection.Open();

            var items = new List<HotelDo>();
            using (var command = _dbConnection.CreateCommand())
            {
                command.CommandType = CommandType.StoredProcedure;
                command.CommandText = "dbo.HotelGetAll";

                using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
                {
                    while (reader.Read())
                    {
                        bool.TryParse(reader["IsOnline"].ToString(), out var isOnline);

                        items.Add(new HotelDo()
                        {
                            Id = SqlExtension.ReaderToIntConverter(reader["Id"]),
                            Rid = SqlExtension.ReaderToStringConverter(reader["RIDHotel"]),
                            Name = SqlExtension.ReaderToStringConverter(reader["Name"]),
                            CodeCountry = SqlExtension.ReaderToStringConverter(reader["CodeCountry"]),
                            LinkPms = SqlExtension.ReaderToStringConverter(reader["LinkPms"]),
                            IdPms = SqlExtension.ReaderToNullableIntConverter(reader["IdPms"]),
                            IdBrand = SqlExtension.ReaderToNullableIntConverter(reader["IdBrand"]) ?? 0,
                            LinkResaWeb = SqlExtension.ReaderToStringConverter(reader["LinkResaWeb"]),
                            IsOnline = isOnline
                        });
                    }
                }
            }
            return items;
        }
        catch (Exception e)
        {
            var errorMessage = $"HotelRepository GetAllHotels";
            _loggerService.Trace(LogSeverity.Error, errorMessage, e);

            throw new DalException() { Source = errorMessage, };
        }
        finally
        {
            _dbConnection.Close();
        }
    }
}

感谢您的帮助

1 个答案:

答案 0 :(得分:0)

因此,根据您的掌握情况,我为您设置了一个测试框架。我已删除了一些次要的重要组成部分。

在您跳入之前,我只想给我2美分,如果您不知道如何做这种事情,您似乎或多或少地处于C#的初级职位或较少的经验,并且还发现了自己在一个或多或少成熟的公司中都不在乎开发方面,因为一个被发现的项目就被扔了,说您没有足够的资源来完善代码库。

  1. 仅测试具有业务价值的事物(如果制止失败,您可以在逻辑上付出代价)
  2. 更快地交付内容将使您看起来像程序员一样好(业务中没有人对测试方法有误解)
  3. 努力学习,获取经验,享有良好的声誉,不要害怕一旦开始感到无聊就离开地狱。

主要

void Main()
{
    var idIndex = 0;
    var ids = new string[] { "1", "2" };

    var mockDataReader = new Mock<IDataReader>();
    mockDataReader.SetupSequence(x => x.Read()).Returns(true).Returns(true).Returns(false);
    mockDataReader.SetupGet(x => x["Id"]).Returns(() => ids[idIndex]).Callback(() => idIndex++);

    var mockParameters = new Mock<IDataParameterCollection>();

    var mockCommand = new Mock<IDbCommand>();
    mockCommand.SetupGet(x => x.Parameters).Returns(mockParameters.Object);
    mockCommand.Setup(x => x.ExecuteReader(CommandBehavior.CloseConnection)).Returns(mockDataReader.Object);

    var mockConnection = new Mock<IDbConnection>();
    mockConnection.Setup(x => x.CreateCommand()).Returns(mockCommand.Object);

    var repo = new HotelRepository(mockConnection.Object);

    var result = repo.Find("search");

    Assert.Equal("1", result[0].Id);
    Assert.Equal("2", result[1].Id);
}

存储库

public class HotelRepository
{
    private readonly IDbConnection _dbConnection;

    public HotelRepository(IDbConnection dbConnection)
    {
        _dbConnection = dbConnection;
    }

    public List<Pony> Find(string text)
    {
        _dbConnection.Open();

        var items = new List<Pony>();
        using (var command = _dbConnection.CreateCommand())
        {
            command.CommandType = CommandType.StoredProcedure;
            command.CommandText = "dbo.HotelSearchByRidOrName";

            command.Parameters.Add(new SqlParameter("@Text", text));

            using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
            {
                while (reader.Read())
                {
                    items.Add(new Pony
                    {
                        Id = reader["Id"].ToString()
                    });
                }
            }
        }
        return items;
    }
}

只是哑巴小马

public class Pony {
    public string Id { get; set; }
}
相关问题