【C#编程最佳实践 一】单元测试实践

简介: 【C#编程最佳实践 一】单元测试实践

工作三周了,深深理解了学校和工作中处理问题的不同,从武哥这里得到了四字经验:最佳实践,而这个最佳实践是先投入工作,而后获取结论,改变之前先理论后实践的做法。于是产生了写一系列的博文的想法,积累工作中的经验,希望以后少踩坑,本篇为开篇第一篇,涉及到我这两周真正有所实践的学习–单元测试。本系列最佳实践每一篇博文都长期更新,而且每一篇都会分两方面来讲:1,知识的通用性,2,知识在工作中的最佳实践。

单元测试

注意:本篇只针对C#,适用版本vs2015

定义

是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,Java里单元指一个类,C#中,一个方法,一个类,一个窗口的测试。即单元测试。单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中 要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。

一些常识

测试范围:单元测试一般针对公共方法,例如public修饰的方法

测试人:编写者自身

测试时机:在函数编写之前,并随着功能增加不断增加测试用例

通用操作

创建和运行

1,创建单元测试项目

2,依据要测试的项目,各级对应创建相应的文件夹

3,在文件夹下添加单元测试

4,注意待测方法前要加[Test Method],否则不能生成单元测试

5,第一次测试的时候要生成解决方案,然后才能运行或调试单元测试

常用测试方法

常用测试方法测试:

Assert.IsNull() 测试指定的对象是否为空引用,如果为空,则测试通过;

Assert.IsNotNull() 测试指定的对象是否为非空,如果不为空,则测试通过;

Assert.IsTrue() 测试指定的条件是否为True,如果为True,则测试通过;

Assert.AreEqual() 测试指定的值是否相等,如果相等,则测试通过;

对于不同返回类型需要做哪些测试

**对于int型:**Assert.AreEqual()方法来测试是否和预期值相等,一般测试是否为1

对于String类型的:先测是否为null---Assert.IsNull(),然后测是否为空串Assert.AreEqual(),最后测和预期值是否相符Assert.AreEqual()

对于list类型:先测是否为null–Assert.IsNull(),然后测list.Count>0是否为真Assert.IsTrue() ,最后测返回列表个数list.Count是否和预期相同Assert.AreEqual()

最佳实践

随着单元测试的深入,持续进行,会涉及到各个方面:首先,DAO层的单元测试最佳实践

最佳实践规则

1,单元测试原子性,没有switch,没有分支,总体来说,圈复杂度为1–ps(vs2015可安装CodeMetrics插件检测圈复杂度)

2,单一职责,指行为不是方法,类例如数据库里的增删改查,CRU和SafeCRU,一个整体行为,而不是多个方法,这样可以减少代码量,而且也对整个行为进行了测试

3,独立无耦合,单元测试间不能相互调用,否则有可能出现无限循环错误

4,属性值不共享,每个独立单元都要准备自己的数据,虽然复用低,但是能保证一致

5,测试驱动开发,单元测试越早越好,我之后写代码也要记住这一点

最佳实践结构

依照个人经验总结以下结构:

1,测试类内准备的公共数据

该测试准备数据修饰为private static

1,公共待测数据:公有的,各个方法都会使用到的测试数据

2,公共的预期值:公有的,约定好的各个待测方法的执行预期值

2,测试类内准备的继承自父类方法的测试

由于一些类可能会共用一些方法,所以可以抽象出一个公共父类,这样我们把所有公共父类所有的方法单独拿出来测试,这样每一个子类测试的时候测试到公有父类的方法可以减少错误的发生,甚至可以直接copy代码

该测试方法修饰为 public void 待测方法名()注意,一定要为public,否则测试无法进行

1,该方法私有的待测数据:私有的,该方法测试需要用到的数据 类型一般为var

2,该方法私有的业务测试:私有的,该方法核心功能的测试(例如一个更新操作函数,就不需要对其它诸如创建等做过多assert)

3,销毁为测试准备的所有数据(公有+私有)

3,测试类内准备的自身实现的方法的测试

不是从父类继承的公用方法,也主要包括三个步骤

该测试方法修饰为 public void 待测方法名()注意,一定要为public,否则测试无法进行

1,该方法私有的待测数据:私有的,该方法测试需要用到的数据 类型一般为var

2,该方法私有的业务测试:私有的,该方法核心功能的测试(例如一个更新操作函数,就不需要对其它诸如创建等做过多assert)

3,销毁为测试准备的所有数据(公有+私有)

最佳实践操作

tip:还可以做代码覆盖率检测哦。利用vs自带的代码覆盖率检测可以看到有哪些方法还没有被测试

DAO层进行的最佳实践

dao层一般用于和数据库的存储过程进行交互,操作无外乎增删改查,且大多数的增删都是通过id来操作,而更新和删除则是通过id和一些相应的参数来实现。

公有的准备数据

对应于准备数据的最佳实践:一般先不做公共准备数据,而是先写各个方法自己存在的准备数据,最后发现统一的可以抽象成公共数据。经过实践,我们可以这么准备数据

#region 公共的准备数据
        // *************************************************************公共的准备数据
        private static int age= 2345;   //例如下边的方法用到了age,所以抽象出来
        /// <summary>
        /// 创建方法,如果每个测试方法都用到了Create,则把它抽象出来实现复用(但不能再将其视为单元测试方法)
        /// </summary>
        /// <returns></returns>
        private int Create(Guid id)
        {
            var model = Build(id);
            return UserDao.Instance.Create(model);
        }
        /// <summary>
        /// 实例构造方法
        /// </summary>
        /// <returns></returns>
        private User Build(Guid id)
        {
            return new User()
            {
                ID = id,
                UserName = "tianmaolin",
                Age=age
             };
        }
        #endregion 公共的准备数据
        #region 公共的预期值
        //*************************************************************公共的预期参数值
        private static int checkDataRight = 1;
        #endregion 公共的预期值

对方法进行单元测试

#region 继承自父类的方法进行的单元测试
        //********************继承自父类的方法进行的单元测试***********************************
        /// <summary>
        /// 基于id测试用户的增删改查
        /// <param name="id"/>
        /// </summary>
        [TestMethod]
        public void UserCRUD()
        {
            //准备数据==========================================================SetUp
            var id = Guid.NewGuid();
            var model = Build(id);
            var create = Create(id);
            //准备好的更新状态
            var modelupdate = new User()
            {
                ID = id,
                UserName = "tttttttt",
            };
            //测试创建============================================================ C
            //测试是否创建成功
            Assert.AreEqual(checkDataRight, create);
            //测试是否查询成功=====================================================R
            var read = UserDao.Instance.GetById(id);
            Assert.IsNotNull(read);  //非空测试
            Assert.IsTrue(read.ID == id);//Guid一致性测试
            Assert.IsTrue(read.UserName == "tianmaolin");//创建者姓名一致    
            Assert.IsTrue(read.Age == age);//创建者年龄一致性测试
            //测试是否更新成功(依据新model更新) =================================== U
            var update = UserDao.Instance.Update(modelupdate);
            Assert.AreEqual(checkDataRight, update);//更新成功测试
            var readupdate = UserDao.Instance.GetById(id);//更新后获取
            Assert.IsTrue(read.UserName == "tttttttt"),创建者姓名更新测试      
            //测试删除=============================================================D
            var delete = UserDao.Instance.Delete(id);
            Assert.AreEqual(checkDataRight, delete);//是否删除成功
            var readagain = UserDao.Instance.GetById(id);//再次查询
            Assert.IsNull(readagain);//为空则删除成功
            //====================================================================TearDown
        }
        #endregion 继承自父类的方法进行的单元测试

踩过的坑

1,如果两张表有主外键关联关系,对从表的数据进行单元测试的时候先要创建主表的数据,最后还要记得销毁主表里的数据,例如student的插入依赖class。

2,写单元测试之前,一定要看代码的具体实现,我写代码发现存储过程里的操作之前有查重操作,然而自己并没有看好,导致每次都插入失败,血淋淋的教训,一定要先看好待测函数的功能实现,避免采坑!!!,尤其是涉及到模糊查询的时候,创建第二条数据的时候一定要改变要求非重复的值,否则会创建失败。

3,涉及到更新的单元测试,提前准备好更新数据,还得注意哪些数据无需更新,不是所有传入的参数都是要更新的,不要导致一直报错

建议测试过程

1,先看函数要实现什么功能,一定要理解他要的输入和期望的输出,如果有分支流程还得注意准备两套数据,如果有更新要提前准备更新数据

2,再看存储过程需要什么参数,存储过程里是否有查重等条件限定,要创建满足条件限定的准备数据

3,然后要准备好数据,发现多个方法有公有数据,统一拿出来,创建这种通用操作抽离出来

4,测试之后一定要记得销毁数据

5,出错要耐心调试,注意表间关系

相关文章
|
2月前
|
XML 前端开发 C#
C#编程实践:解析HTML文档并执行元素匹配
通过上述步骤,可以在C#中有效地解析HTML文档并执行元素匹配。HtmlAgilityPack提供了一个强大而灵活的工具集,可以处理各种HTML解析任务。
177 19
|
6月前
|
人工智能 自然语言处理 安全
学不会编程也能写测试?AI让测试更平权
在传统的软件开发体系中,测试常被划分为“技术型测试”(如自动化、性能、安全)和“业务型测试”(如功能验证、用户体验)。前者掌握技术话语权,后者则更多依赖经验和流程规范。然而,随着大语言模型(LLM)等AI技术的迅猛发展,这一固有格局正被悄然打破:
203 10
|
3月前
|
监控 算法 C#
C#与Halcon联合编程实现鼠标控制图像缩放、拖动及ROI绘制
C#与Halcon联合编程实现鼠标控制图像缩放、拖动及ROI绘制
519 0
|
8月前
|
机器学习/深度学习 设计模式 测试技术
Python 高级编程与实战:构建自动化测试框架
本文深入探讨了Python中的自动化测试框架,包括unittest、pytest和nose2,并通过实战项目帮助读者掌握这些技术。文中详细介绍了各框架的基本用法和示例代码,助力开发者快速验证代码正确性,减少手动测试工作量。学习资源推荐包括Python官方文档及Real Python等网站。
|
11月前
|
人工智能 自然语言处理 前端开发
CodeArena:在线 LLM 编程竞技场!用于测试不同开源 LLM 的编程能力,实时更新排行榜
CodeArena 是一个在线平台,用于测试和比较不同大型语言模型(LLM)的编程能力。通过实时显示多个 LLM 的代码生成过程和结果,帮助开发者选择适合的 LLM,并推动 LLM 技术的发展。
287 7
CodeArena:在线 LLM 编程竞技场!用于测试不同开源 LLM 的编程能力,实时更新排行榜
|
11月前
|
存储 安全 编译器
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
823 12
|
11月前
|
监控 数据管理 测试技术
API接口自动化测试深度解析与最佳实践指南
本文详细介绍了API接口自动化测试的重要性、核心概念及实施步骤,强调了从明确测试目标、选择合适工具、编写高质量测试用例到构建稳定测试环境、执行自动化测试、分析测试结果、回归测试及集成CI/CD流程的全过程,旨在为开发者提供一套全面的技术指南,确保API的高质量与稳定性。
|
11月前
|
数据管理 测试技术 持续交付
软件测试中的自动化测试策略与最佳实践
在当今快速迭代的软件开发环境中,自动化测试已成为确保软件质量和加速产品上市的关键手段。本文旨在探讨软件测试中的自动化测试策略,包括选择合适的自动化测试工具、构建有效的自动化测试框架以及实施持续集成和持续部署(CI/CD)。通过分析自动化测试的最佳实践,本文为软件开发团队提供了一系列实用的指南,以优化测试流程、提高测试效率并减少人为错误。
302 4
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
248 3
|
设计模式 C# 图形学
Unity 游戏引擎 C# 编程:一分钟浅谈
本文介绍了在 Unity 游戏开发中使用 C# 的基础知识和常见问题。从 `MonoBehavior` 类的基础用法,到变量和属性的管理,再到空引用异常、资源管理和性能优化等常见问题的解决方法。文章还探讨了单例模式、事件系统和数据持久化等高级话题,旨在帮助开发者避免常见错误,提升游戏开发效率。
476 4