[英]Database function Mock is not working in c# web api unit testing
这是我的控制器方法
[HttpPost]
[Authorize]
[Route(RouteConfig.Routes.LovList.contactStatus)]
public IHttpActionResult ContactStatusList()
{
try
{
var result = new DBClass.HeroDb().GetList(
DBClass.DBConstants.ListConstants.query_Contact_Status);
return Json(new Models.Response(
Models.ResponseMessages.Success,
result)
);
}
catch(System.Data.SqlClient.SqlException)
{
return InternalServerError();
}
catch(System.Exception ex)
{
Logger.Error(ex, ex.Message, ex.StackTrace);
return InternalServerError();
}
}
这是我的测试用例方法
[TestMethod()]
public void ContactStatusListTest()
{
Mock<DBClass.HeroDb> mock = new Mock<DBClass.HeroDb>();
mock.Setup(x => x.GetList(DBClass.DBConstants.ListConstants.query_Contact_Status))
.Returns(CreateContactList());
var result = new ListController().ContactStatusList();
Models.Response response = (Models.Response)result;
Assert.AreEqual(response.Message, Models.ResponseMessages.Success);
Assert.IsNotNull(response.Data);
}
public System.Data.DataTable CreateContactList()
{
DataTable table = new DataTable();
table.Columns.Add("ContactStatus");
DataRow row1 = table.NewRow();row1["ContactStatus"] = "Contacted"; table.Rows.Add(row1);
DataRow row2 = table.NewRow(); row2["ContactStatus"] = "Not Contacted"; table.Rows.Add(row2);
DataRow row3 = table.NewRow(); row3["ContactStatus"] = "Contacted"; table.Rows.Add(row3);
return table;
}
我试图在我的测试方法中模拟GetList()函数,但它不起作用。 控制器方法给出内部服务器错误。 因为控制将要
var result = new DBClass.HeroDb()
.GetList(DBClass.DBConstants.ListConstants.query_Contact_Status);
此行和 db 对象在这里为空。 请帮助,因为我是单元测试用例构建的初学者。
首先让我们先打下基础:单元测试不同于集成测试。
在这种情况下,这是对控制器方法ContactStatusList
的单元测试。 您只测试了这个方法,并且通过尝试模拟您的HeroDb
对象实际上正确地做了一些事情。 请注意,您决定模拟此对象,因为这是一个依赖项。
问题是你设置了 Mock 但你没有使用它,因为在你的ContactStatusList
方法中你调用了new DBClass.HeroDb()
。
第二个问题是你试图模拟一个类。 这实际上是可能的,但是您要模拟的所有类的方法都必须声明为虚拟的。 因此,实际上最好模拟一个接口。
这个接口应该在你的ListController
的构造函数中接收。 在您的 Web 项目的常规执行中,在启动时注入该接口的实例,但在单元测试中将您的模拟提供给ListController
的构造函数。
记住这条规则:任何依赖都应该被你的控制器的构造函数接收
这是您的界面和 DbHero 类
public interface IDbHero
{
IEnumerable<Contact> GetList(QueryContactStatus status);
}
public class DbHero : IDbHero
{
public IEnumerable<Contact> GetList(QueryContactStatus status)
{
// Implementation here
}
}
现在这是你的控制器:
[ApiController]
[Route("api/[controller]")]
public class ListController: ControllerBase
{
private readonly IHeroDb _heroDb;
public ListController(IHeroDb heroDb)
{
_heroDb = heroDb ?? throw new ArgumentNullException(nameof(heroDb));
}
[HttpPost]
[Authorize]
[Route(RouteConfig.Routes.LovList.contactStatus)]
public IHttpActionResult ContactStatusList()
{
try
{
var result = _heroDb.GetList(DBClass.DBConstants.ListConstants.query_Contact_Status);
return Json(new Models.Response(
Models.ResponseMessages.Success,
result)
);
}
catch(System.Exception ex)
{
Logger.Error(ex, ex.Message, ex.StackTrace);
throw;
}
}
}
请注意,我删除了仅捕获SqlException
的块,因为无论如何,如果您有未处理的异常,服务器将返回内部服务器错误,因此如果您甚至不记录错误,则捕获它是没有用的。 同样在我throw
的第二个 catch 块中,服务器也会自动返回内部服务器错误。 如果您处于调试模式,这可能会很方便,因为您会得到完整的异常返回给您,但是如果您返回InternalServerError()
,即使在调试中您也不会得到任何信息,您必须检查日志...
在Startup.cs
类的ConfigureServices
方法中,注入IDbHero
接口的实现。 请注意,这是一个范围服务,这意味着将为每个 HTTP 请求创建一个新实例。 就我个人而言,我从不将我的数据库访问层作为单例注入,因为这可能会导致一些问题,具体取决于该层的实现方式。 例如,EF Core 的DbContext与单例模式不兼容。
services.AddScoped<IDbHero>(_ => new DBClass.HeroDb(Configuration.GetConnectionString("DbHeroConnectionString")));
我不知道你如何处理与数据库的连接,因为在你的代码示例中没有提到连接字符串,但我会做类似上面的事情。
您的连接字符串来自您的appsettings.json
配置文件
"ConnectionStrings": {
"DbHeroConnectionString": "YourConnectionString"
}
现在要在单元测试中使用模拟对象,只需执行以下操作:
[TestMethod()]
public void ContactStatusList_ShouldReturnData_WhenCalled()
{
// ARRANGE
var mock = new Mock<IHeroDb>();
mock.Setup(x => x.GetList(DBClass.DBConstants.ListConstants.query_Contact_Status))
.Returns(CreateContactList());
var sut = new ListController(mock.Object);
// ACT
var result = sut.ContactStatusList();
// ASSERT
Models.Response response = (Models.Response)result;
Assert.AreEqual(response.Message, Models.ResponseMessages.Success);
Assert.IsNotNull(response.Data);
}
注意这里的几件事:
您的单元测试名称:这应该显示 3 件事:
例如,您可以测试当参数值不正确时方法是否返回错误。 这应该在另一个单元测试中测试
单元测试总是在 3 部分ARRANGE
、 ACT
和ASSERT
。 在每个测试中编写它总是一个好习惯,这样你就可以更好地组织你的代码
sut
表示System Under Test
:这是您要测试的内容,所有其他依赖项(例如您的DbHero
层)都应该被DbHero
。
现在下一步是编写单元测试来测试您的DbHero.GetList
。 这次您将创建DbHero
类的真实实例(不是模拟),因为这是您要测试的内容:这是您的sut 。
请注意,我在测试方面处于中级水平,因此我向您展示的是我从同事那里学到的良好实践。 但也许有更多经验的开发人员可以提出比我更好的实践。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.