【单元测试】NUnit框架了解及使用

一般测试流程

单元测试-》集成测试-》系统测试-》域测试

其中单元测试、集成测试、系统测试区别:

一、测试方法不同

1、单元测试属于白盒测试范畴。

2、集成测试属于灰盒测试范畴。

3、系统测试属于黑盒测试范畴。

二、考察范围不同

1、单元测试主要测试单元内部的数据结构、逻辑控制、异常处理等。

2、集成测试主要测试模块之间的接口和接口数据传递关系,以及模块组合后的整体功能。

3、系统测试主要测试整个系统相对于需求的符合度。

三、评估基准不同

1、单元测试的评估基准主要是逻辑覆盖率。

2、集成测试的评估基准主要是接口覆盖率。

3、系统测试的评估基准主要是测试用例对需求规格的覆盖率。

什么是域测试?
域测试是一种软件测试方法,它涉及提供一组输入并评估应用程序的合适输出。域测试是一种确保软件程序在一定范围内获取数据并且输出符合用户期望的技术。

域测试是一个有用的工具。使用少量输入检查系统的输出,以确保系统不接受错误数据并且不接受超出范围的输入值。它是最常见的白盒测试技术之一。它还确保系统不会接受超出设置参数的输入。

领域测试是否需要领域知识?
一个人很难在他们不熟悉的领域中有效地开展工作。因此,对我们将要工作的领域有一个基本的了解是至关重要的。这也很重要,因为如果我们没有基本的领域知识,我们就无法有效地完成任务:

域的例子
(1) 银行领域

测试人员应该对银行概念有基本的了解,例如登录、账单支付和转账。如果测试人员知识渊博,他将能够快速执行域测试。

(2) 零售领域

要成功执行域测试,测试人员必须了解各个级别的工作原理。示例:仓库管理、店内解决方案和其他零售相关服务。

(3) 医疗领域

一个了解领域知识的人可以有效地管理医疗保健系统。如果某人不具备医疗保健领域的专业知识,则存在多种风险。

何为单元测试(unit test)

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。

其实,对“单元”的定义取决于自己。如果你正在使用函数式编程,一个单元最有可能指的是一个函数。你的单元测试将使用不同的参数调用这个函数,并断言它返回了期待的结果;在面向对象语言里,下至一个方法,上至一个类都可以是一个单元(从一个单一的方法到一整个的类都可以是一个单元)。

众所周知bug越早发现,解决问题的代价就越小
在这里插入图片描述
在这里插入图片描述

单元测试怎么做

单元测试的实现方式包括:人工静态检查、动态执行跟踪
人工静态检查
就是通常所说的“代码走读”,主要是保证代码逻辑的正确性动态执行跟踪:就是把程序代码运行起来,检查实际的运行结果和预期结果是否一致

人工静态检查人工静态检查包含的主要内容:

  • 检查算法的逻辑正确性
  • 模块接口的正确性检查
  • 输入参数有没有作正确性检查
  • 调用其他方法接口的正确性
  • 异常错误处理
  • 保证表达式、SQL语句的正确性
  • 检查常量或全局变量使用的正确性
  • 程序风格的一致性、规范性
  • 检查代码注释是否完整

动态执行跟踪
动态执行跟踪需要编写测试脚本调用业务代码进行测试,为了更好的管理维护测试脚本,一般会采用单元测试框架来管理,不同的语言有不同的单元测试框架:
Java:JUnit、TestNG
Python:UintTest、pyTest单元测试的一个重要的衡量标准就是代码覆盖率,尽量做到代码的全覆盖。常见单元测试覆盖标准:

  • 语句覆盖
  • 分支覆盖
  • 条件覆盖
  • 分支-条件覆盖
  • 条件组合覆盖
  • 路径覆盖

入门示例:针对开发人员编写的实现计算操作的方法进行单元测试

# 开发人员编写的业务代码
class CalUtil:
    """计算器"""

    @staticmethod
    def add(x, y):
        """加法"""
        return x + y

    @staticmethod
    def sub(x, y):
        """减法"""
        return x - y

    @staticmethod
    def mul(x, y):
        """乘法"""
        return x * y

    @staticmethod
    def div(x, y):
        """除法"""
        return x / y


# 单元测试脚本
import unittest
from test_ut.cal import CalUtil

class TestCal(unittest.TestCase):
    def test_add_01(self):
        # 测试数据
        x = 1
        y = 2
        expect = 3

        # 调用被测方法
        result = CalUtil.add(x, y)
        print(f"result={result}")

        # 断言
        self.assertEqual(expect, result)

    def test_add_02(self):
        # 测试数据
        x = 1
        y = -1
        expect = 0

        # 调用被测方法
        result = CalUtil.add(x, y)
        print(f"result={result}")

        # 断言
        self.assertEqual(expect, result)

    # ...

驱动代码,桩代码和Mock代码
驱动代码是用来调用被测函数的,而桩代码和Mock代码是用来代替被测函数调用的真实代码的。
在这里插入图片描述
驱动代码、桩代码和Mock代码三者的逻辑关系

驱动代码(Driver)指调用被测函数的代码,在单元测试过程中,驱动模块通常包括调用被测函数钱的数据准备、调用被测函数以及验证相关结果三个步骤。

代码桩(Stub)是用来代替真是代码的临时代码。比如,某个函数A的内部实现中调用了一个尚未实现的函数B,为了对函数A的逻辑进行测试,那么就需要模拟一个函数B,这个模拟的函数B的实现就是所谓的桩代码。
伪代码:

在这里插入图片描述

为了实现函数A的全路径覆盖,你需要控制不同的测试用例中函数B的返回值,那么桩函数B的伪代码就应该是:
  当执行第一个测试用例的时候,桩函数B应该返回true,而执行第二个测试用例的时候,桩函数B应该返回false。
  这样就覆盖了被测试函数A的if-else的两个分支。

在这里插入图片描述

桩代码的应用首先起到了隔离和补齐的作用,使被测代码能够独立编译、链接,并独立运行。同时,桩代码还具有控制被测函数执行路径的作用。

编写桩代码通常需要遵守以下三个原则:

  1. 桩函数要具有与原函数完全相同的原形,仅仅是内部实现不同,这样测试代码才能正确链接到桩函数;
  2. 用于实现隔离和补齐的桩函数比较简单,只需保持原函数的声明,加一个空的实现,目的是通过编译链接;
  3. 实现控制功能的桩函数是应用最广泛的,要根据测试用例的需要,输出合适的数据作为被测函数的内部输入。

Mock代码和桩代码的本质区别是:测试期待结果的验证(Assert and Expectiation)。

  • 对于Mock代码来说,我们的关注点是Mock方法有没有被调用,以书面样的参数被调用,被调用的次数,以及多个Mock函数的先后调用顺序。所以,在使用Mock代码的测试中,对于结果的验证(也就是assert),通常出现在Mock函数中。
  • 对于桩代码来说,我们的关注点是利用Stub来控制被测函数的执行路径,不会去关注Stub是否被调用以及怎么样被调用。所以,你在使用Stub的测试中,对于结果的验证,通常出现在驱动代码中。

自动化测试工具

断言Assert
当你对类,模块或者方法的可以接受的输入和输出有着很明确的定义和认识,当程序的输出结果和输入不匹配时,
你想让程序有一个明确的返回。比如:我明确地知道1+1=2,我现在就想知道我输入1+1会不会得到2。如果不是程序返回一个错误。这时候我们通常会使用到断言。

断言就是专门用来验证输出和期望是否一致的一个工具。在内容的实现上,它是通过比较一个实际值actual和一个期望值expected来实现的。

常见断言参见
Assert Class
假设Assume
在这里插入图片描述

NUnit
NUnit 是 JUnit 的 .NET 版,支持所有 .NET 语言,完全使用 C# 编写,并进行完全重新设计以利用很多高级的 .NET 语言特性,例如定制属性以及其他相关的反射功能。

NUnit使用
安装

适用于测试生成器的 NUnit 扩展
在这里插入图片描述
手动创建测试项目
在这里插入图片描述
自动创建测试项目
1、右键某个要测试的方法
在这里插入图片描述
需要public修饰
在这里插入图片描述

2、填写好测试框架等信息
在这里插入图片描述
3、右键运行

基于C#的单元测试
MSTest使用方法
从代码创建单元测试

相关好文

那些年,我们写过的无效单元测试

猜你喜欢

转载自blog.csdn.net/weixin_44231544/article/details/123224608