首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

首次在WebAPI中写单元测试

编程知识
2024年08月16日 09:43

xUnit

这次我使用的是xUnit测试框架,而不是VS自带的MSTest框架。在添加新建项目时选择xUnit测试项目就行了。

目前只体验到了一个差别,即xUnit可以使用特性向测试方法传参,而不用在测试方法中一个赋值语句一个个去定义参数,这是比较方便的。

单元测试有一个好处,就是一次性可以获得所测试的很多接口的失败信息。如果使用swagger去测试接口,只能去启动项目,输入密码鉴权,然后一个个发请求。遇到一个错误处理一个接口,比较麻烦。单元测试可以把所有接口的报错信息展示在测试窗口,而且是缓存的,不用启动项目,只需要点击一下测试按钮,就把所有接口测试了。我在迁移接口时,有一个控制器一次性迁移了14个接口,单元测试通过了6个,失败了8个,失败的都列出了错误消息,这就很舒服了。经过了几天的使用,我发现这比到swagger或postman中手动测试接口方便太多了。

image

这里的失败基本都是迁移数据库结构引起的,我申请修改结构后,就又有几个通过了测试。修改进度如何,看起来很直观。到目前为止几天了,测试仍然没有全部通过,单元测试起到了很好的监控作用。

image

点击失败的测试,可以看到调用堆栈,跳转到运行失败的那一行代码。这使得修改起来很方便。

单元测试环境准备

我写的是控制器方法中action的单元测试。但是一般来说,控制器和Service层会注入许多服务,而action依赖于这些服务。在使用依赖注入时,单元测试要如何处理这种情况?

可以和ASP.NET core的做法一样。它准备了一个依赖注入容器,那我也准备一个依赖注入容器。WebAPI还构造了一个web主机。但是单元测试是独立运行的,就不需要创建一个web主机了。在单元测试项目种添加了一个TestBase基类,用于创建容器,注册服务,以供测试方法使用。

//测试环境
public class TestBase
{
	//依赖注入容器
	public IServiceCollection Services;
	//从容器获取服务
	public IServiceProvider Provider;

	public TestBase()
	{
		//创建容器
		Services = new ServiceCollection();
		//....注册服务
		Provider = Services.BuildServiceProvider();
	}
}

然后向容器注册我们需要的服务,比如常见的MemoryCache IWebHostEnvironment ISqlSugarClient XXXService。我们就不需要在测试方法中使用new运算符创建服务类实例,而是可以直接从容器中获得了。

//注册服务层
Services.AddScoped<ISingleWellService, SingleWellService>();
//注册缓存
Services.AddScoped<IMemoryCache, MemoryCache>(service =>
{
	return new MemoryCache(new MemoryCacheOptions());
});
//注册SqlSuger
Services.AddScoped<ISqlSugarClient>(service =>
{
	return new SqlSugarClient(new ConnectionConfig()
	{
		ConnectionString = "Data Source=XXX",
		DbType = DbType.Oracle,
		IsAutoCloseConnection = true,
		InitKeyType = InitKeyType.Attribute,
	});
});
//注册环境变量
Services.AddScoped<IWebHostEnvironment, WebHostEnvironment>();

IWebHostEnvironment

为了使用这个接口需要引入包Microsoft.Extensions.DependencyInjection.Abstractions。这个接口一般是WebApplicationBuilder创建的,在涉及文件读写时经常用到。但是单元测试项目中没有builder,我就自己添加了一个IWebHostEnvironment实现类,并注册到容器中。缺点是还要把可能需要的WebAPI项目中的文件,比如模板文件、数据文件也复制到单元测试项目中。

public class WebHostEnvironment : IWebHostEnvironment
    {
        public string WebRootPath { get => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot");  }
        public IFileProvider WebRootFileProvider { get; set; }
        public string EnvironmentName { get; set; }
        public string ApplicationName { get; set; }
        public string ContentRootPath { get => AppDomain.CurrentDomain.BaseDirectory; }
        public IFileProvider ContentRootFileProvider { get; set; }
    }

ICurentUser

这个自定义接口一般是在请求处理管道中存储身份验证后的相关信息。单元测试中不同的接口可能需要不同的user,比如一个流程中,不同角色调用同一个接口。ICurentUser同样也是注册到容器中的,然后在service层注入。具体的业务方法中根据这个角色的不同执行不同逻辑。

我比较疑惑的是,单元测试又该怎么注入呢?要注意的是,不同测试方法的ICurentUser是不同的。要知道从容器中获取service,容器自动帮我们挑选了合适够构造方法。但是这里由于角色的不同,不能直接从容器获取准备好的角色存根。难道我们要手动构造service传入controller中吗?我是有听说mokq,但不知道怎么用来模拟多个ICurentUser。

为控制器添加单元测试

添加一个HomeControllerUnitTest类,并继承于前面定义的基类TestBase。我们应该在构造函数中从容器取出相应的服务以供使用。

public class HomeControllerUnitTest:TestBase
    {
        HomeController homeController;
        public HomeControllerUnitTest()
        {
			//从容器注入服务
            homeController = new HomeController(
				Provider.GetRequiredService<IHomeService>(), 
				Provider.GetRequiredService<IWebHostEnvironment>());
        }
}

接着,向测试类添加单元Action的测试方法。一般都是三步

  • 准备数据 Arrange
  • 调用测试方法 Act
  • 断言结果 Assert

也就是AAA模式。

[Theory(DisplayName = "测试XXX")]
[InlineData("xxx", "xxx", 1,30)]
public void Test_GetData(string wId, string tId, int page, int rows)
{
	var data = homeController.GetData(wId, tId, page, rows).Result;
	Assert.True(data.Tag);
}

在实际使用时,我会每添加一个测试,就运行未运行的测试。头一天看一下哪些测试没通过,这里一般是数据库结构不对,然后申请修改数据库。第二天再运行失败的测试验证。不会每次运行全部测试。

From:https://www.cnblogs.com/ggtc/p/18359336
本文地址: http://shuzixingkong.net/article/1149
0评论
提交 加载更多评论
其他文章 如何快速自学开源项目?试试我的诀窍
在讲解项目的过程中,我发现很多同学并不清楚如何快速学习项目、以及如何快速阅读源码。今天这篇文章,我就带大家领悟这些技能,以后再自学项目会轻松很多~
如何快速自学开源项目?试试我的诀窍 如何快速自学开源项目?试试我的诀窍 如何快速自学开源项目?试试我的诀窍
SMCA:港中文提出注意力图校准的DETR加速方案 | ICCV 2021
为了加速DETR收敛,论文提出了简单而有效的Spatially Modulated Co-Attention(SMCA)机制,通过在初始边界框位置给予较高的协同注意力响应值的约束来构建DETR的回归感知协同注意力。此外,将SMCA扩展为多头注意力和尺度选择注意力后,对比DETR可以实现更好的性能(1
SMCA:港中文提出注意力图校准的DETR加速方案 | ICCV 2021 SMCA:港中文提出注意力图校准的DETR加速方案 | ICCV 2021 SMCA:港中文提出注意力图校准的DETR加速方案 | ICCV 2021
Go 监控告警入门 Opentelemetry
探索 Go 语言中 Opentelemetry 与 Prometheus 集成,导出 HTTP 服务指标监控,并最终将 Prometheus 指标可视化到 Grafana 中。
Go 监控告警入门 Opentelemetry Go 监控告警入门 Opentelemetry Go 监控告警入门 Opentelemetry
ZPL Viewer工具网站
新上线的ZPL Viewer网站(地址:[https://zplpreview.com/](https://zplpreview.com/))支持zpl预览,pdf、html、图片转zpl
.NET 轻量化定时任务调度 FreeScheduler
前言 在平时项目开发中,定时任务调度是一项重要的功能,广泛应用于后台作业、计划任务和自动化脚本等模块。 FreeScheduler 是一款轻量级且功能强大的定时任务调度库,它支持临时的延时任务和重复循环任务(可持久化),能够按秒、每天/每周/每月固定时间或自定义间隔执行(CRON 表达式)。 此外
.NET 轻量化定时任务调度 FreeScheduler .NET 轻量化定时任务调度 FreeScheduler .NET 轻量化定时任务调度 FreeScheduler
Xinference实战指南:全面解析LLM大模型部署流程,携手Dify打造高效AI应用实践案例,加速AI项目落地进程
Xinference实战指南:全面解析LLM大模型部署流程,携手Dify打造高效AI应用实践案例,加速AI项目落地进程
Xinference实战指南:全面解析LLM大模型部署流程,携手Dify打造高效AI应用实践案例,加速AI项目落地进程 Xinference实战指南:全面解析LLM大模型部署流程,携手Dify打造高效AI应用实践案例,加速AI项目落地进程 Xinference实战指南:全面解析LLM大模型部署流程,携手Dify打造高效AI应用实践案例,加速AI项目落地进程
Linux/Go环境搭建, HelloWorld运行
package main import &quot;fmt&quot; func main() { fmt.Printf(&quot;Hello,World!!!\n&quot;) } 以上是Go语言的经典HelloWorld程序源代码。 Linux/GO 环境搭建 作者初学Linux/Go时,是通
Linux/Go环境搭建, HelloWorld运行 Linux/Go环境搭建, HelloWorld运行 Linux/Go环境搭建, HelloWorld运行
免杀基础学习记录
前言 参考SecretTeam安全团队的学习记录 什么是免杀? 免杀(Bypass AV, Anti-Virus Evasion)是指恶意软件通过各种手段规避杀毒软件和安全检测系统的识别和拦截,从而在目标系统中成功执行。这种技术不仅用于恶意软件的传播,也被信息安全研究人员用来测试和提升安全防护系统的
免杀基础学习记录 免杀基础学习记录