IntelliTest 通过分析程序中的分支条件来生成 参数化单元测试 的输入。 根据是否可以触发程序的新分支行为来选择测试输入。 分析是一个增量过程。 它在形式化测试输入参数上细化谓词q: I -> {true, false}I。
q 表示 IntelliTest 已观察到的行为集。 最初, q := false由于尚未观察到任何内容。
循环的步骤包括:
IntelliTest 通过使用约束求解器来确定满足
q(i)=false的输入i。 通过构造,输入i将采用以前未看到的执行路径。 最初,这意味着i可以是任何输入,因为尚未发现任何执行路径。IntelliTest 使用所选的输入
i执行测试,并监视测试的执行和正在测试的程序。在执行期间,程序采用由程序的所有条件分支确定的特定路径。 确定执行的所有条件集称为 路径条件,作为谓词
p: I -> {true, false}写入正式输入参数。 IntelliTest 计算此谓词的表示形式。IntelliTest 设置
q := (q or p)。 换句话说,它记录了看到由p表示的路径这一事实。转到步骤 1。
IntelliTest 的约束求解器 可以处理可能出现在 .NET 程序中的所有类型的值:
IntelliTest 会筛选出违反所述假设的输入。
除了直接输入( 参数化单元测试的参数),测试还可以从 PexChoose 静态类中绘制进一步的输入值。 这些选项还确定 参数化模拟的行为。
约束求解器
IntelliTest 使用约束求解器来确定测试的相关输入值和所测试的程序。
IntelliTest 使用 Z3 约束求解器。
动态代码覆盖率
作为运行时监视的副作用,IntelliTest 收集动态代码覆盖率数据。 这称为 动态 ,因为 IntelliTest 只知道已执行的代码,因此它不能像其他覆盖率工具一样为覆盖率提供绝对值。
例如,当 IntelliTest 将动态覆盖率报告为 5/10 基本块时,这意味着覆盖了 10 个块中的 5 个块,其中分析迄今已到达的所有方法中的块总数(而不是测试的程序集中存在的所有方法)为 10。 稍后在分析中,当发现更多可访问的方法时,分子(此示例中的 5)和分母(10)可能分别发生增长。
整数和浮点数
IntelliTest 的约束求解器 确定基元类型的测试输入值,例如 字节、 int、 float 和其他类型,以便触发测试的不同执行路径和所测试的程序。
对象
IntelliTest 可以 创建现有 .NET 类的实例,也可以使用 IntelliTest 自动创建实现特定接口的 模拟对象 ,并根据使用情况以不同的方式行为。
实例化现有类
怎么了?
IntelliTest 在运行测试和受测程序时监视执行的指令。 具体而言,它会监视对字段的所有访问。 然后,它使用 约束求解器 来确定新的测试输入,包括对象及其字段值,以便测试和受测程序以其他有趣的方式运行。
这意味着 IntelliTest 必须创建特定类型的对象并设置其字段值。 如果类 可见 且具有 可见 的默认构造函数,IntelliTest 可以创建类的实例。 如果类的所有字段 都可见,IntelliTest 可以自动设置字段。
如果类型不可见或字段不 可见,IntelliTest 需要帮助创建对象并将其引入有趣的状态,以实现最大代码覆盖率。 IntelliTest 可以使用反射以任意方式创建和初始化实例,但这通常不理想,因为它可能会使对象进入在正常程序执行期间永远不会发生的状态。 相反,IntelliTest 依赖于用户的提示。
能见度
.NET 具有详细的可见性模型:类型、方法、字段和其他成员可以是 私有、 公共、 内部等。
当 IntelliTest 生成测试时,它将尝试仅执行与生成的测试上下文中的 .NET 可见性规则相关的合法作(例如调用构造函数、方法和设置字段)。
规则如下:
内部成员的可见性
- IntelliTest 假定生成的测试有权访问对封闭 PexClass 可见的内部成员。 .NET 具有 InternalsVisibleToAttribute ,可将内部成员的可见性扩展到其他程序集。
PexClass 的私有和家庭成员(在 C# 中受保护)的可见性
公共成员的可见性
- IntelliTest 假定它可以使用 PexClass 上下文中可见的所有导出成员。
参数化模拟对象
如何测试具有接口类型的参数的方法? 还是非密封类? IntelliTest 不知道在调用此方法时将使用哪些实现。 也许测试时甚至没有真正的实现可用。
传统的答案是使用具有显式行为的 模拟对象 。
模拟对象实现接口(或扩展非密封类)。 它并不表示实际实现,而只是允许使用模拟对象执行测试的快捷方式。 其行为是手动定义的,作为使用每个测试用例的一部分。 存在许多工具,使定义模拟对象及其预期行为变得容易,但此行为仍必须手动定义。
IntelliTest 可以生成值,用以代替在模拟对象中硬编码的值。 正如它启用 参数化单元测试一样,IntelliTest 还启用参数化模拟。
参数化模拟具有两种不同的执行模式:
- 选择:在浏览代码时,参数化模拟是其他测试输入的源,IntelliTest 将尝试选择有趣的值
- 重播:执行以前生成的测试时,参数化模拟对象的行为类似于具有预定义行为的存根(换句话说,预定义行为)。
使用 PexChoose 获取参数化模拟的值。
结构
IntelliTest 对 结构 值的推理与处理 对象的方式类似。
数组和字符串
IntelliTest 在运行测试和受测程序时监视已执行的指令。 具体而言,它会观察程序何时依赖于字符串或数组的长度(以及多维数组的下限和长度)。 它还观察程序如何使用字符串或数组的不同元素。 然后,它使用 约束求解器 来确定哪些长度和元素值可能导致测试和受测程序以有趣的方式运行。
IntelliTest 尝试将触发有趣的程序行为所需的数组和字符串的大小降到最低。
获取其他输入
PexChoose 静态类可用于获取测试的其他输入,并可用于实现参数化模拟。
有反馈吗?
在 开发人员社区上发布想法和功能请求。