在C#中进行单元测试

· 浏览次数 : 0

小编点评

单元测试简介 单元测试是软件开发中的一种测试方法,用于验证代码中的最小可测试单元(通常是方法或函数)是否按照预期进行工作。这些单元通常是独立于其他代码部分进行测试的,以确保其正确性和可靠性。 单元测试的主要作用: 1. 确保每个单元能正确执行预期功能。 2. 能够尽快找到Bug的具体位置。 3. 开始测试。 项目搭建 1. 创建一个控制台项目,起名为UnitTesting。 2. 安装Microsoft.Extensions.DependencyInjection包,管理IOC容器。 3. 创建ITimeProvider接口,并创建SystemTimeProvider类去实现这个接口。 4. 创建GreetingService类,依赖ITimeProvider。 5. 使用IOC容器注入服务并调用Greet方法。 测试程序 1. 使用xUnit模版创建单元测试,名为UnitTesting.Test。 2. 添加UnitTesting项目引用,安装Moq包。 3. 编写测试流程,包括Arrange、Act、Assert阶段。 4. 验证返回的消息是否与预期的结果相同。 通过以上步骤,可以完成单元测试的搭建和测试。

正文

单元测试

前言

时隔多个月,终于抽空学习了点新知识,那么这次来记录一下C#怎么进行单元测试,单元测试是做什么的。

我相信大部分刚毕业的都很疑惑单元测试是干什么的?在小厂实习了6个月后,我发现每天除了写CRUD就是写CRUD,几乎用不到单元测试。写完一个功能直接上手去测,当然这只是我个人感受,仅供参考。

然后当我还在抱怨测试好烦的时候,大佬跟我说为什么不用单元测试和集成测试,我这也是有苦说不出。要知道光学会理论知识,没有实践作为基础,都是扯淡,入职这么久还真没用过单元测试,吓得我赶紧去找资料学习。

那么也是通过观看B站某位Up主的视频,然后有点想法写下这篇文章,虽然up主的主题是探究接口的作用和意义,但是视频中也讲解了怎么进行单元测试,所以对于接口理解不够的可以去本文底部观看视频学习。

那么本篇文章就简单的讲解下C#中如何做单元测试,博主也是处于学习阶段,有不对的地方欢迎指出改正。

单元测试简述

单元测试(Unit Testing)是软件开发中的一种测试方法,用于验证代码中的最小可测试单元(通常是方法或函数)是否按照预期进行工作。这些单元通常是独立于其他代码部分进行测试的,以确保其正确性和可靠性。

单元测试的主要作用:

  • 确保每个单元能正确执行预期功能
  • 能够尽快找到Bug的具体位置

开始测试

本文以当前时间去返回早上好、中午好、晚上好来讲解单元测试。

通过传入不同的时间(边界值)来确保代码能够正确处理各种情况以及是否达到了预期的功能。

预期结果为:

  • 早上好...
  • 中午好...
  • 晚上好..

项目搭建

主程序

首先需要创建一个控制台项目,起名为UnitTesting

并安装Microsoft.Extensions.DependencyInjection包,管理IOC容器。

创建ITimeProvider接口,并创建SystemTimeProvider类去实现这个接口

public interface ITimeProvider
{
    int GetHour();
}
//返回当前时间
public class SystemTimeProvider: ITimeProvider
{
    public int GetHour()
    {
        return DateTime.Now.Hour;
    }
}

创建GreetingService

public class GreetingService
{
    private readonly ITimeProvider _timeProvider;

    public GreetingService(ITimeProvider timeProvider)
    {
        _timeProvider = timeProvider;
    }
    /// <summary>
    /// 通过当前时间来打返回问候语
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public string Greet(string name)
    {
        var hour = _timeProvider.GetHour();
        return hour switch
        {
            < 12 => $"Good Morning,{name}",
            < 18 => $"Good Afternoon,{name}",
            _ => $"Good Evening,{name}"
        };
    }
}

Program.cs使用IOC容器注入服务并调用Greet方法

using Microsoft.Extensions.DependencyInjection;
using UnitTesting.Services;

var container = new ServiceCollection();
container.AddSingleton<ITimeProvider,SystemTimeProvider>();
container.AddTransient<GreetingService>();
var services = container.BuildServiceProvider();

var greetingService = services.GetRequiredService<GreetingService>();
var greeting = greetingService.Greet("吗喽");
Console.WriteLine(greeting);

测试程序

xUnit模版创建单元测试,名为UnitTesting.Test,并添加UnitTesting项目引用,还需安装Moq包:

Moq包(全称Mocking Objects in C#,简称Moq)是一个流行的模拟框架,其主要作用在于模拟和验证对象的行为,以支持更加可靠和可重复的测试,简单来讲就是模拟创建对象。

回到GreetingService类,这里使用Rider提供的快捷方式创建测试类,当然也可以手动创建。如图:

image

测试流程:

  • Arrange:准备阶段,创建ITimeProvider的模拟对象provider,并指定时间参数且调用GetHour()方法,使用这个模拟对象创建GreetingService实例。
  • Act:执行阶段,调用GreetingServiceGreet方法
  • Assert:断言阶段,验证返回的消息是否与预期的结果相同。
using JetBrains.Annotations;
using Moq;
using UnitTesting.Services;

namespace UnitTesting.Tes.Services;

[TestSubject(typeof(GreetingService))]
public class GreetingServiceTests
{
    [Fact]
    public void GreetReturnsMorningMessage()
    {
        // Arrange
        var provider = new Mock<ITimeProvider>();
        provider.Setup(x => x.GetHour()).Returns(10);
        var service = new GreetingService(provider.Object);

        // Act
        var message = service.Greet("吗喽");

        // Assert
        Assert.Equal("Good Morning,吗喽", message);
    }
    [Fact]
    public void GreetReturnsAfternoonMessage()
    {
        // Arrange
        var provider = new Mock<ITimeProvider>();
        provider.Setup(x => x.GetHour()).Returns(15);
        var service = new GreetingService(provider.Object);

        // Act
        var message = service.Greet("吗喽");

        // Assert
        Assert.Equal("Good Afternoon,吗喽", message);
    }
    [Fact]
    public void GreetReturnsEveningMessage()
    {
        // Arrange
        var provider = new Mock<ITimeProvider>();
        provider.Setup(x => x.GetHour()).Returns(20);
        var service = new GreetingService(provider.Object);

        // Act
        var message = service.Greet("吗喽");

        // Assert
        Assert.Equal("Good Evening,吗喽", message);
    }
}

效果截图

主程序没什么好讲的,通过当前时间返回问候语。

image

测试程序通过3个测试方法测试了3种情况,早上好、中午好、晚上好,并全部测试通过。

image

总结

本文讲解了如何创建单元测试,并且通过单元测试来测试Greet方法,在传入不同的时间参数的情况下,判断是否满足3种情况。

本文提到了IOC容器、依赖注入、Moq、xUnit等知识点。

参考链接

与在C#中进行单元测试相似的内容:

在C#中进行单元测试

单元测试 前言 时隔多个月,终于抽空学习了点新知识,那么这次来记录一下C#怎么进行单元测试,单元测试是做什么的。 我相信大部分刚毕业的都很疑惑单元测试是干什么的?在小厂实习了6个月后,我发现每天除了写CRUD就是写CRUD,几乎用不到单元测试。写完一个功能直接上手去测,当然这只是我个人感受,仅供参考

ONNX Runtime入门示例:在C#中使用ResNet50v2进行图像识别

ONNX Runtime简介 ONNX Runtime 是一个跨平台的推理和训练机器学习加速器。ONNX 运行时推理可以实现更快的客户体验和更低的成本,支持来自深度学习框架(如 PyTorch 和 TensorFlow/Keras)以及经典机器学习库(如 scikit-learn、LightGBM、

在C#中使用RabbitMQ做个简单的发送邮件小项目

在C#中使用RabbitMQ做个简单的发送邮件小项目 前言 好久没有做项目了,这次做一个发送邮件的小项目。发邮件是一个比较耗时的操作,之前在我的个人博客里面回复评论和友链申请是会通过发送邮件来通知对方的,不过当时只是简单的进行了异步操作。 那么这次来使用RabbitMQ去统一发送邮件,我的想法是通过

【OpenVINO™】在 C# 中使用OpenVINO™ 部署PP-YOLOE实现物体检测

PP-YOLOE是基于PP-YOLOv2的优秀单级无锚模型,超越了各种流行的YOLO模型。PP-YOLOE有一系列型号,命名为s/m/l/x,通过宽度乘数和深度乘数进行配置。PP-YOLOE避免使用特殊的运算符,如可变形卷积或矩阵NMS,以便友好地部署在各种硬件上。 在本文中,我们将使用OpenVI...

【OpenVINO™】在C#中使用 OpenVINO™ 部署 YOLOv10 模型实现目标

最近YOLO家族又添新成员:YOLOv10,YOLOv10 提出了一种一致的双任务方法,用于无nms训练的YOLOs,它同时带来了具有竞争力的性能和较低的推理延迟。此外,还介绍了整体效率-精度驱动的模型设计策略,从效率和精度两个角度对YOLOs的各个组成部分进行了全面优化,大大降低了计算开销,增强了...

Selenium 自动化浏览器,解决懒加载的网页获取问题

Selenium 自动化浏览器,解决懒加载的网页获取问题。可以用于爬虫这些 在使用 Selenium WebDriver 进行自动化测试时,可以通过设置日志级别来控制输出的日志信息。在 C# 中,可以通过以下方式来禁用 Selenium WebDriver 输出的日志信息: 导入 OpenQA.Se

C#中接口的显式实现与隐式实现及其相关应用案例

C#中接口的显式实现与隐式实现 最近在学习演化一款游戏项目框架时候,框架作者巧妙使用接口中方法的显式实现来变相对接口中方法进行“密封”,增加实现接口的类访问方法的“成本”。 接口的显式实现和隐式实现: 先定义一个接口,接口中有这两个方法。 public interface ICanSingSong

.NET周报【1月第2期 2023-01-13】

国内文章 【ASP.NET Core】按用户等级授权 https://www.cnblogs.com/tcjiaan/p/17024363.html 本文介绍了ASP.NET Core如何按照用户等级进行授权。 在 C# 9 中使用 foreach 扩展 https://www.cnblogs.co

WPF/C#:在WPF中如何实现依赖注入

本文先介绍依赖注入的概念,再解释为什么要进行依赖注入,最后通过 WPF Gallery 这个项目学习如何在WPF中使用依赖注入。

C#事件

C#事件 概述 定义 类或对象可以通过事件向其他类或对象通知发生的相关事情。 发送(或引发)事件的类称为“发布者”,接收(或处理)事件的类称为“订阅者”。 事件是一种特殊的多播委托,是委托实例变量, 事件只能在类的内部定义,只能从声明它的类中进行调用。 外部只能订阅和取消订阅事件。 事件的组成部分