- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章浅谈.Net Core后端单元测试的实现由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
单元测试一直都是"好处大家都知道很多,但是因为种种原因没有实施起来"的一个老大难问题。具体是否应该落地单元测试,以及落地的程度, 每个项目都有自己的情况.
本篇为个人认为"如何更好地写单元测试", 即更加 偏向实践向 中夹杂一些理论的分享.
下列示例的单元测试框架为 xUnit , Mock库为 Moq 。
优点有很多, 这里提两点我个人认为的很明显的好处 。
通常在进行新功能/模块的开发或者是重构的时候,测试会进行回归测试原有的已存在的功能,以验证以前实现的功能是否仍能按预期运行.
使用单元测试,可在每次生成后,甚至在更改一行代码后重新运行整套测试, 从而可以很大程度减少回归缺陷.
当代码紧密耦合或者一个方法过长的时候,编写单元测试会变得很困难。当不去做单元测试的时候,可能代码的耦合不会给人感觉那么明显。为代码编写测试会自然地解耦代码,变相提高代码质量和可维护性.
3A分别是"arrange、act、assert", 分别代表一个合格的单元测试方法的三个阶段 。
一个单元测试方法可读性是编写测试时最重要的方面之一。 在测试中分离这些操作会明确地突出显示调用代码所需的依赖项、调用代码的方式以及尝试断言的内容. 。
所以在进行单元测试的编写的时候, 请使用注释标记出3A的各个阶段的, 如下示例 。
[Fact]public async Task VisitDataCompressExport_ShouldReturnEmptyResult_WhenFileTokenDoesNotExist(){ // arrange var mockFiletokenStore = new Mock<IFileTokenStore>(); mockFiletokenStore .Setup(it => it.Get(It.IsAny<string>())) .Returns(string.Empty); var controller = new StatController( mockFiletokenStore.Object, null); // act var actual = await controller.VisitDataCompressExport("faketoken"); // assert Assert.IsType<EmptyResult>(actual);}
尽管私有方法可以通过反射进行直接测试,但是在大多数情况下,不需要直接测试私有的private方法, 而是通过测试公共public方法来验证私有的private方法.
可以这样认为:private方法永远不会孤立存在。更应该关心的是调用private方法的public方法的最终结果.
如果一个类/方法,有很多的外部依赖,造成单元测试的编写困难。那么应该考虑当前的设计和依赖项是否合理。是否有部分可以存在解耦的可能性。选择性重构原有的方法,而不是硬着头皮写下去. 。
3.4 避免多个断言 。
如果一个测试方法存在多个断言,可能会出现某一个或几个断言失败导致整个方法失败。这样不能从根本上知道是了解测试失败的原因.
所以一般有两种解决方案 。
[Theory][InlineData(null)][InlineData("a")]public void Add_InputNullOrAlphabetic_ThrowsArgumentException(string input){ // arrange var stringCalculator = new StringCalculator(); // act Action actual = () => stringCalculator.Add(input); // assert Assert.Throws<ArgumentException>(actual);}
当然如果是对对象进行断言, 可能会对对象的多个属性都有断言。此为例外.
一般有两种。比如针对 UserController 下方法的单元测试应该统一放在 UserControllerTest 或者 UserController_Test 下 。
单元测试方法名 。
单元测试的方法名应该具有可读性,让整个测试方法在不需要注释说明的情况下可以被读懂。格式应该类似遵守如下 。
<被测试方法全名>_<期望的结果>_<给予的条件>// 例子[Fact]public void Add_InputNullOrAlphabetic_ThrowsArgumentException(){ ...}
编写.Net Core的单元测试绕不过要选择一个单元测试的框架, 三大单元测试框架中 。
三大测试框架发展至今已是大差不差, 很多时候选择只是靠个人的喜好.
个人偏好 xUnit 简洁的断言 。
// xUnitAssert.True()Assert.Equal()// MsTestAssert.IsTrue()Assert.AreEqual()
客观地功能性地分析三大框架地差异可以参考如下 。
https://anarsolutions.com/automated-unit-testing-tools-comparison 。
官方仓库 。
https://github.com/moq/moq4 。
Moq是一个非常流行的模拟库, 只要有一个接口它就可以动态生成一个对象, 底层使用的是Castle的动态代理功能. 。
基本用法 。
在实际使用中可能会有如下场景 。
public class UserController{ private readonly IUserService _userService; public UserController(IUserService userService) { _userService = userService; } [HttpGet("{id}")] public IActionResult GetUser(int id) { var user = _userService.GetUser(id); if (user == null) { return NotFound(); } else { ... } }}
在进行单元测试的时候, 可以使用 Moq 对 _userService.GetUser 进行模拟返回值 。
[Fact]public void GetUser_ShouldReturnNotFound_WhenCannotFoundUser(){ // arrange // 新建一个IUserService的mock对象 var mockUserService = new Mock<IUserService>(); // 使用moq对IUserService的GetUs方法进行mock: 当入参为233时返回null mockUserService .Setup(it => it.GetUser(233)) .Return((User)null); var controller = new UserController(mockUserService.Object); // act var actual = controller.GetUser(233) as NotFoundResult; // assert // 验证调用过userService的GetUser方法一次,且入参为233 mockUserService.Verify(it => it.GetUser(233), Times.AtMostOnce());}
官方仓库 。
https://github.com/AutoFixture/AutoFixture 。
AutoFixture是一个假数据填充库,旨在最小化3A中的 arrange 阶段,使开发人员更容易创建包含测试数据的对象,从而可以更专注与测试用例的设计本身.
基本用法 。
直接使用如下的方式创建强类型的假数据 。
[Fact]public void IntroductoryTest(){ // arrange Fixture fixture = new Fixture(); int expectedNumber = fixture.Create<int>(); MyClass sut = fixture.Create<MyClass>(); // act int result = sut.Echo(expectedNumber); // assert Assert.Equal(expectedNumber, result);}
上述示例也可以和测试框架本身结合,比如xUnit 。
[Theory, AutoData]public void IntroductoryTest( int expectedNumber, MyClass sut){ // act int result = sut.Echo(expectedNumber); // assert Assert.Equal(expectedNumber, result);}
Visual Studio提供了完备的单元测试的支持,包括运行. 编写. 调试单元测试。以及查看单元测试覆盖率等.
如下功能需要Visual Studio 2019 Enterprise版本,社区版不带这个功能.
如何查看覆盖率 。
主要 。
使用EF Core过程中,如何mock DbSet是一个绕不过的坎.
方法一 。
参考如下链接的回答进行自行封装 。
https://stackoverflow.com/questions/31349351/how-to-add-an-item-to-a-mock-dbset-using-moq 。
方法二(推荐) 。
使用现成的库(也是基于上面的方式封装好的) 。
仓库地址:
https://github.com/romantitov/MockQueryable 。
使用范例 。
// 1. 测试时创建一个模拟的List<T>var users = new List<UserEntity>(){ new UserEntity{LastName = "ExistLastName", DateOfBirth = DateTime.Parse("01/20/2012")}, ...};// 2. 通过扩展方法转换成DbSet<UserEntity>var mockUsers = users.AsQueryable().BuildMock();// 3. 赋值给给mock的DbContext中的Users属性var mockDbContext = new Mock<DbContext>();mockDbContext .Setup(it => it.Users) .Return(mockUsers);
使用RestEase/Refit的场景 。
如果使用的是 RestEase 或者 Refit 等第三方库,具体接口的定义本质上就是一个interface,所以直接使用moq进行方法mock即可.
并且建议使用这种方式.
IHttpClientFactory 。
如果使用的是.Net Core自带的 IHttpClientFactory 方式来请求外部接口的话,可以参考如下的方式对 IHttpClientFactory 进行mock 。
https://www.thecodebuzz.com/unit-test-mock-httpclientfactory-moq-net-core/ 。
由于ILogger的LogError等方法都是属于扩展方法,所以不需要特别的进行方法级别的mock.
针对平时的一些使用场景封装了一个帮助类, 可以使用如下的帮助类进行Mock和Verify 。
public static class LoggerHelper{ public static Mock<ILogger<T>> LoggerMock<T>() where T : class { return new Mock<ILogger<T>>(); } public static void VerifyLog<T>(this Mock<ILogger<T>> loggerMock, LogLevel level, string containMessage, Times times) { loggerMock.Verify( x => x.Log( level, It.IsAny<EventId>(), It.Is<It.IsAnyType>((o, t) => o.ToString().Contains(containMessage)), It.IsAny<Exception>(), (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()), times); } public static void VerifyLog<T>(this Mock<ILogger<T>> loggerMock, LogLevel level, Times times) { loggerMock.Verify( x => x.Log( level, It.IsAny<EventId>(), It.IsAny<It.IsAnyType>(), It.IsAny<Exception>(), (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()), times); }}
使用方法 。
[Fact]public void Echo_ShouldLogInformation(){ // arrange var mockLogger = LoggerHelpe.LoggerMock<UserController>(); var controller = new UserController(mockLogger.Object); // act controller.Echo(); // assert mockLogger.VerifyLog(LogLevel.Information, "hello", Times.Once());}
TDD是测试驱动开发(Test-Driven Development)的英文简称. 一般是先提前设计好单元测试的各种场景再进行真实业务代码的编写,编织安全网以便将Bug扼杀在在摇篮状态.
此种开发模式以测试先行,对开发团队的要求较高, 落地可能会存在很多实际困难。详细说明可以参考如下 。
https://www.guru99.com/test-driven-development.html 。
https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices 。
https://www.kiltandcode.com/2019/06/16/best-practices-for-writing-unit-tests-in-csharp-for-bulletproof-code/ 。
https://github.com/AutoFixture/AutoFixture 。
到此这篇关于浅谈.Net Core后端单元测试的实现的文章就介绍到这了,更多相关.Net Core 单元测试内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://www.cnblogs.com/baoshu/p/14500273.html 。
最后此篇关于浅谈.Net Core后端单元测试的实现的文章就讲到这里了,如果你想了解更多关于浅谈.Net Core后端单元测试的实现的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
主要思想是将 EF Core nuget 包添加到 .NET Core 库项目,然后在一堆应用程序(例如 ASP.NET Core、Win 服务、控制台应用程序)中使用该库,而无需在每个应用程序中配置
我想要实现的是编写一个简单的.net核心后台工作程序(.net core 3.1)的代码,在该工作程序作为Windows服务运行时,我在其中将数据写入SQL Server数据库(通过EF Core 3
关于 .Net Core SDK download page 二进制文件有什么用?它与安装程序有何不同? 最佳答案 二进制文件是 .NET Core 的编译代码。它们拥有运行 .NET Core 所需
.NET Core 和 Entity Framework Core 之间的区别?我们可以在 .NET Core 中使用 Entity Framework Core 吗?两者都有什么优势? 最佳答案 E
.NET Core 和 ASP.NET Core 到底有什么区别? 它们是相互排斥的吗?我听说 ASP.NET Core 是基于 .NET Core 构建的,但它也可以基于完整的 .NET 框架构建。
我对 ASP.NET Core 开发完全陌生。我正在尝试使用单个模型和 mysql 创建一个简单的 asp.net core Web api 来存储模型数据,然后我想使用 Swagger 将其作为 R
.NET Core 和 Entity Framework Core 之间的区别?我们可以在 .NET Core 中使用 Entity Framework Core 吗?两者都有什么优势? 最佳答案 E
好吧,作为一个新的 .net 开发生态系统,我有点迷失在核心工具、版本等方面。 有人可以解释我之间的区别吗 VS 2015 核心工具预览版 x - See here .NET Core/SDK 与否
我已阅读有关如何通过信号器核心集线器从后台服务向客户端发送通知的文档。如何从客户端接收到后台服务的通知? 后台服务应该只是一个单例。 public class Startup { public
关闭。这个问题是opinion-based .它目前不接受答案。 想改善这个问题吗?更新问题,以便可以通过 editing this post 用事实和引文回答问题. 4年前关闭。 Improve t
非常简单的问题: 我正在尝试创建一个像这样的谓词构建器: var predicate = PredicateBuilder.False(); 但似乎在Net Core和EF Core中不可用。
在 .NET Core 自包含应用程序 中...我们需要在 project.json 中指定运行时 (RID) 我们希望我们的应用程序针对...发布为什么会这样? .NET Core 是跨平台的,与我
如何用 iCloud Core Data 替换我现有的 Core Data?这是我的持久商店协调员: lazy var persistentStoreCoordinator: NSPersistent
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 2 年前。 Improv
今天我正在学习新的 ASP.net 核心 API 3.1,我想将我的旧网站从 MVC4 转移到 Web API。除了一件事,一切都很好。数据库连接。在我的旧网站中,我为每个客户端(10/15 数据库)
我在 Visual Studio 2015 Update 3 和 .NET Core 1.0 中工作。我有一个 .NETCoreApp v1.0 类型的 Web API 项目。当我添加一个 .NET
我一直在尝试遵循 Ben Cull ( http://benjii.me/2016/06/entity-framework-core-migrations-for-class-library-proj
当我打开我的 vs 代码程序时,我收到以下消息: 无法找到 .NET Core SDK。 .NET Core 调试将不会启用。确保 .NET Core SDK 已安装并且在路径上。 如果我安装甚至卸载
我偶然发现了一个非常奇怪的问题。每当 Web 应用程序启动时,dotnet.exe 都会使用相当多的内存(大约 300M)。然而,当它触及某些部分时(我感觉这与 EF Core 使用有关),它会在短时
ASP.NET Core Web (.NET Core) 与 ASP.NET Core Web (.NET Framework) 有什么区别? .NET Framework 是否提供 similar
我是一名优秀的程序员,十分优秀!