IntelliTest 使你能够尽早发现 bug,并降低测试维护成本。 使用自动化和透明测试方法,IntelliTest 可以为 .NET 代码生成候选测试套件。 测试套件的生成可以通过您指定的 正确性属性 进一步优化。 随着测试中的代码的发展,IntelliTest 甚至会自动改进测试套件。
注释
Visual Studio 2026 中弃用 IntelliTest。 在 Visual Studio 2022 中,仅 .NET Framework 支持 IntelliTest,并且仅限于 Visual Studio Enterprise。 仅对 .NET 6 的支持仅限于预览版。
注释
在 Visual Studio 2022 中,仅 .NET Framework 支持 IntelliTest,并且仅限于 Visual Studio Enterprise。 仅对 .NET 6 的支持仅限于预览版。
特征测试 IntelliTest 使你能够根据传统单元测试套件来确定代码的行为。 此类测试套件可用作回归套件,形成解决与重构旧代码或不熟悉代码相关的复杂性的基础。
引导式测试输入生成 IntelliTest 使用开放式代码分析和约束求解方法自动生成精确的测试输入值;通常不需要任何用户干预。 对于复杂的对象类型,它自动生成工厂。 可以通过扩展和配置工厂以满足你的要求来指导测试输入生成。 在代码中,被指定为断言的正确性属性会被自动使用,以进一步指导测试输入的生成。
IDE 集成 IntelliTest 已完全集成到 Visual Studio IDE 中。 在测试套件生成期间收集的所有信息(例如自动生成的输入、代码的输出、生成的测试用例及其通过或失败状态)将显示在 Visual Studio IDE 中。 无需离开 Visual Studio IDE 即可轻松循环访问修复代码并重新运行 IntelliTest。 测试可以作为单元测试项目保存到解决方案中,然后由 Visual Studio 测试资源管理器自动检测。
补充现有测试做法 使用 IntelliTest 补充你可能已遵循的任何现有测试做法。
如果要测试:
- 基元数据或基元数据数组的算法:
- 写入 参数化单元测试
- 复杂数据的算法,例如编译器:
- 让 IntelliTest 首先生成数据的抽象表示形式,然后将其馈送给算法
- 让 IntelliTest 使用 自定义对象创建 和数据固定器生成实例,然后调用算法
- 数据容器:
- 现有基本代码:
- 使用 Visual Studio 的 IntelliTest 向导开始生成一组 参数化单元测试(PUT)
IntelliTest 的 Hello World
IntelliTest 查找与测试程序相关的输入,这意味着你可以使用它生成著名的 Hello World! 字符串。 这假定已创建基于 C# MSTest 的测试项目,并添加了对 Microsoft.Pex.Framework 的引用。 如果使用其他测试框架,请创建一个 C# 类库,并参阅有关如何设置项目的测试框架文档。
以下示例针对命名 值 的参数创建两个约束,以便 IntelliTest 生成所需的字符串:
using System;
using Microsoft.Pex.Framework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public partial class HelloWorldTest {
[PexMethod]
public void HelloWorld([PexAssumeNotNull]string value) {
if (value.StartsWith("Hello")
&& value.EndsWith("World!")
&& value.Contains(" "))
throw new Exception("found it!");
}
}
编译和执行后,IntelliTest 将生成一组测试,例如以下集:
- ""
- "\0\0\0\0\0"
- “Hello”
- "\0\0\0\0\0\0"
- “你好\0”
- "你好\0\0"
- “Hello\0World!”
- “Hello World!”
注释
对于生成问题,请尝试将 Microsoft.VisualStudio.TestPlatform.TestFramework 和 Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions 引用替换为对 Microsoft.VisualStudio.QualityTools.UnitTestFramework 的引用。
阅读 使用 IntelliTest 生成单元测试 ,了解生成的测试的保存位置。 生成的测试代码应包括一个测试,例如以下代码:
[TestMethod]
[PexGeneratedBy(typeof(global::HelloWorldTest))]
[PexRaisedException(typeof(Exception))]
public void HelloWorldThrowsException167()
{
this.HelloWorld("Hello World!");
}
太简单了!
其他资源:
- 阅读 MSDN 杂志上的此概述
重要属性
- PexClass 标记包含 PUT 的类型
- PexMethod 标记 PUT
- PexAssumeNotNull 标记非 null 参数
using Microsoft.Pex.Framework;
[..., PexClass(typeof(Foo))]
public partial class FooTest {
[PexMethod]
public void Bar([PexAssumeNotNull]Foo target, int i) {
target.Bar(i);
}
}
- PexAssemblyUnderTest 将测试项目绑定到项目
- PexInstrumentAssembly 指定要检测的程序集
[assembly: PexAssemblyUnderTest("MyAssembly")] // also instruments "MyAssembly"
[assembly: PexInstrumentAssembly("Lib")]
重要的静态帮助程序类
- PexAssume 评估假设(输入筛选)
- PexAssert 评估断言
- PexChoose 会生成新选项(其他输入)
- PexObserve 将实时值记录到生成的测试
[PexMethod]
void StaticHelpers(Foo target) {
PexAssume.IsNotNull(target);
int i = PexChoose.Value<int>("i");
string result = target.Bar(i);
PexObserve.ValueForViewing<string>("result", result);
PexAssert.IsNotNull(result);
}
局限性
本部分介绍 IntelliTest 的限制:
非确定性
IntelliTest 假定分析的程序是确定性的。 如果不是,IntelliTest 会循环,直到它达到探索边界。
如果程序依赖于 IntelliTest 无法控制的输入,IntelliTest 会认为该程序是非确定性的。
IntelliTest 控制输入,这些输入被提供给 参数化单元测试 并从 PexChoose 获取。 从这个意义上讲,对非托管或未检测代码的调用结果也被视为已检测程序的“输入”,但 IntelliTest 对它们无能为力。 如果程序的控制流取决于来自这些外部源的特定值,IntelliTest 无法将程序“引导”到之前发现的区域。
此外,如果外部源中的值在重新运行程序时发生更改,则程序被视为非确定性。 在这种情况下,IntelliTest 会失去对程序执行的控制,并且其搜索效率低下。
有时,这种情况发生时并不明显。 请考虑以下示例:
- GetHashCode() 方法的结果由非托管代码提供,不可预测。
- System.Random 类使用当前系统时间来提供真正的随机值。
- System.DateTime 类提供当前时间,该时间不受 IntelliTest 的控制。
Concurrency
IntelliTest 不处理多线程程序。
本机代码
IntelliTest 不了解本机代码,例如通过 P/Invoke 调用的 x86 指令。 它不知道如何将此类调用转换为可传递给 约束求解器的约束。 即使是对于 .NET 代码,它也只能分析由其工具处理的代码。 IntelliTest 无法检测 mscorlib 的某些部分,包括反射库。 无法对DynamicMethod进行监测。
建议的解决方法是具有测试模式,其中此类方法位于动态程序集中的类型中。 但是,即使某些方法未进行检测,IntelliTest 也会尝试尽可能多地覆盖已检测的代码。
平台
IntelliTest 仅支持 X86(32 位).NET 框架。
语言
原则上,IntelliTest 可以分析以任何 .NET 语言编写的任意 .NET 程序。 但是,在 Visual Studio 中,它仅支持 C# 。
符号推理
IntelliTest 使用自动 约束求解器 来确定与测试和所测试的程序相关的值。 但是,约束求解器的能力是限制的,并且始终是有限的。
堆栈跟踪不正确
由于 IntelliTest 在每个检测方法中捕获和“重新引发”异常,因此堆栈跟踪中的行号不正确。 这是设计“重新抛出”指令的一个限制。