unit-testing - 在没有实际数据库调用的情况下在具有 N 层的 Web api 中进行单元测试
问题描述
我正在为我的 Web API 应用程序编写单元测试,使用 .net Core 创建。我使用 MSTest 和 MOQ 进行模拟。
它有很多层,如下所示。
Controller => Manager 类 => Facade 类 => Contract 类
我在我模拟的控制器中提供实体。
[TestMethod]
public void InvoiceUpdateFinanceTest_Success()
{
var mock = mockFactory.OutstandingInvoicesDetailsMock();
dataController.InvoiceUpdateFinance(mock);
Assert.IsTrue(true);
}
合同类正在创建数据库连接实例并进行数据库调用
合约层代码示例如下
public class GradeWiseSlab : IMaintainContract
{
IDbConnection _db = new DBModel().InstanceCreation();
DynamicParameters dp = new DynamicParameters();
public MaintainContractEntities MaintainContractEntities { get; set; }
public void SaveData()
{
dp.Add("@N_VENDOR_ID", MaintainContractEntities.VendorId);
dp.Add("@N_CONTRACT_SUB_TYPE_ID", MaintainContractEntities.SubContractType);
dp.Add("@N_PAYMENT_TERM", MaintainContractEntities.PaymentTerm);
dp.Add("@S_TRANSACTION_CURRENCY", MaintainContractEntities.TransactionCurrency);
dp.Add("@S_DOC_NAME", MaintainContractEntities.DocNames);
dp.Add("@S_APPROVAL_STATUS", MaintainContractEntities.ApproalStatus);
dp.Add("@D_FROM_DATE", MaintainContractEntities.FromDate);
dp.Add("@D_TO_DATE", MaintainContractEntities.ToDate);
this._db.Query(Constant.SP_GRADE_WISE_SLAB, dp, commandTimeout: 0, commandType: CommandType.StoredProcedure);
}
我如何编写覆盖所有层的测试用例,即从控制器到合同,但没有实际的数据库要触摸?
我没有使用实体框架,我使用的是 Dapper 库。
解决方案
如果您使用的是 EntityFramework,EF 现在有一个 InMemoryDatabase。
但情况似乎并非如此。
我必须重写你的一些“术语”,因为你没有显示很多代码并且术语有点混乱。
Controller (WebApiLayer)=> Manager classes (BusinessLogic Layer) => DataLayer(这是对存储过程的调用实际存在的地方)。根据您的描述,我会说我的“DataLayer”是您的“Contract”层......恕我直言,“Contract”是一个令人困惑和模棱两可的名称......对于数据层来说不是一个好名字。但我离题了。
通过上述设计,您可以将一些 IDataLayer 对象(例如,IEmployeeDataLayer)接口注入到您的经理(EmployeeManager:IEmployeeManager)中。您将拥有一个“真实的”具体的 EmployeeTheRealOneDataLayer 对象……并且您还可以模拟 IEmployeeDomainDataLayer .. 以便对您的 EmployeeManager 对象进行单元测试。
我实际上在这里有一个“关闭”示例:
public class EmployeeManager : IEmployeeManager
{
private readonly ILogger<EmployeeManager> logger;
private readonly IEmployeeDataLayer iedl;
public EmployeeManager(ILoggerFactory loggerFactory, IEmployeeDataLayer iedl)
{
this.logger = loggerFactory.CreateLogger<EmployeeManager>();
this.iedl = iedl;
}
使用我的单元测试代码,我将模拟 IEmployeeDataLayer ,以便能够测试 EmployeeManager 类。
您的问题是..我如何在不访问真实数据库的情况下测试“真实” EmployeeDataLayer。
这是我不认为它存在的突破点。数据层中的所有代码都被编码为(真的)访问数据库。
所以这就是圣战开始的地方。有人会说“将它连接到 sqlexpress 数据库”......但是恕我直言,当这些停止成为单元测试(不仅仅在内存中运行)......而是成为更多功能测试时。
由于您没有使用 EF,因此您可能必须做出决定。对您的业务逻辑层编写单元测试“没问题”......并“跳过”DataLayer。事实上,我会将以下属性放在我的数据层类中。
您的另一个选择是转向更像这样的东西:(内存中的EF)
https://docs.microsoft.com/en-us/ef/core/providers/in-memory/?tabs=dotnet-core-cli
就个人而言,(实际上我在不使用 EF 时会这样做)(而且我也使用 Dapper,因为 Dapper 性能良好)......我跳过了 UNIT 测试数据层。(强调“单元”测试)
但是我们的 QA 会针对真实的数据库引擎(想想 SqlSExpress)编写 FUNCTIONAL 测试。这里需要注意的是,他们必须能够(我们每周选择一次)完全删除/刷新“qa 数据库”......并从种子数据中重建它。这可以防止人们编写功能测试......在表格上寻找硬编码的代理(主键)......但强制通过唯一约束来寻找项目。又名,它可以防止与 qa 功能测试数据库中的特定数据“过于结合”)。(注意,这与 EF In Memory Database 的概念类似……因为当您使用 EF In Memory db 启动单元测试时,它在启动时基本上是“空的”)。
附言
您可以在此处查看更多面向 EF 的项目设置: https ://github.com/granadacoder/oracle-ef-issues-demo/tree/master/src
聚苯乙烯
我并不是说上面的想法是一个详尽的可能性列表。但是当其他想法开始越过“只是在记忆中”的界限时......这就是这些迷你圣战开始的时候。我对我们公司的“记忆中”线非常坚定。我们曾经有很多“MyProject.Tests.csproj”,这可以指单元测试或功能测试。现在我“强烈鼓励”将项目命名为“MyProject.UnitTests.csproj”或“MyProject.FunctionalTests.csproj”以消除歧义。不是每个人都喜欢我的“在沙滩上画线”的方法。
推荐阅读
- mongodb - 如何在 $lookup 上获取单个字段?
- visual-studio-code - VSCode remote-ssh 密码提示不出现
- three.js - Three.js / 反应三纤维 | 如何在 BufferGeometry(costum SphereGeometry)上均匀分布 PointsMaterial 并将顶点与图像数据进行比较
- linux - Umask 在 open() 系统调用中?
- django - Django Querset 提取 PK
- c++ - C++ 如何用 ncurses.h 移动文本?
- javascript - 在页脚组件之前先加载网站页面内容
- asp.net - EF 包括未正确获取数据
- java - 如何在 OpenXava 中创建文档模块?
- android - 应用程序启动时前台服务出错