unit-testing - 如何使用 moq 对存储库和模拟数据库进行单元测试
问题描述
我独自在一个没有任何单元测试的现有项目上入职。在进行任何重构之前,我的第一个目标是覆盖 100% 的代码。我想避免任何回归。
我已经阅读了如何模拟 sqlconnection 还是应该重构代码?但正如您在下面看到的那样,我的情况完全不同,因为我需要做的不仅仅是 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();
}
}
}
谢谢您的帮助
解决方案
因此,根据您所拥有的,我设置了一个测试框架供您遵循。为了简洁起见,我删除了不太重要的组件。
在你加入之前,我只想给我 2 美分,如果你不知道如何做这种事情,你似乎或多或少处于初级职位或 C# 经验较少,并且发现自己处于或多或少不关心开发部门的成熟公司,因为一个未被发现的项目只是被你扔了,这说明你没有很多资源来改进代码库。
- 只测试具有商业价值的东西(如果它刹车的话,你能不能给它定价)
- 更快地交付东西会让你作为一名程序员看起来更好(业务中没有人对测试协方差感兴趣)
- 努力学习,获得你的经验,良好的声誉,一旦你开始感到无聊,不要害怕离开那里。
主要的
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; }
}
推荐阅读
- java - 改变位置的Java 2D-Array问题
- reactjs - 我如何获得当前反应条件的路线?
- python - 如何为 python 更快地制作用于在大量 .txt 文件中查找链接的代码?
- python - 如何使用 matplotlib 在 Repl.it 中显示绘图?
- python - 编写通用代码来构造元组和命名元组
- python - Docker 连接到“现有”数据库,但显示为空
- c# - DLL 中的函数总是返回 null
- python - (Python 3.2): if 语句不满足条件时退出
- mysql - InnoDB 全文搜索死锁
- angular - 发生未处理的异常:无法从“/project 到 docker 中找到模块“@angular-devkit/build-angular”