在传统的单元测试中,测试由以下几个方面组成:
下面是一个示例测试结构:
[Test]
void MyTest() {
// data
ArrayList a = new ArrayList();
// method sequence
a.Add(5);
// assertions
Assert.IsTrue(a.Count==1);
Assert.AreEqual(a[0], 5);
}
IntelliTest 通常可以自动确定更多通用 参数化单元测试的相关参数值,这些测试提供方法调用和断言序列。
测试生成器
IntelliTest 通过选择被测实现的方法序列来执行,并在检查派生数据的断言时为这些方法生成输入,从而生成测试用例。
参数化单元测试直接在其正文中声明方法调用序列。
当 IntelliTest 需要构造对象时,对构造函数和工厂方法的调用将根据需要自动添加到序列中。
参数化单元测试
参数化单元测试 (PUT)是采用参数的测试。 与传统单元测试(通常是封闭方法)不同,PUT 采用任意一组参数。 这很简单吗? 好的,在此基础上,IntelliTest 将尝试生成一组最小化的输入,以完全涵盖可从测试访问的代码。
PexMethod 自定义属性以类似于 MSTest(或 NUnit、xUnit)的方式定义 PUT。 PUT 是在用 PexClass 标记的类中逻辑分组的实例方法。 以下示例演示存储在 MyPexTest 类中的简单 PUT:
[PexMethod]
void ReplaceFirstChar(string target, char c) {
string result = StringHelper.ReplaceFirstChar(target, c);
Assert.AreEqual(result[0], c);
}
其中 ReplaceFirstChar 是替换字符串的第一个字符的方法:
class StringHelper {
static string ReplaceFirstChar(string target, char c) {
if (target == null) throw new ArgumentNullException();
if (target.Length == 0) throw new ArgumentOutOfRangeException();
return c + target.Substring(1);
}
}
在此测试中,IntelliTest 可以自动为 PUT 生成输入,这些输入涵盖被测代码的许多执行路径。 涵盖不同执行路径的每个输入都将作为单元测试“序列化”:
[TestMethod, ExpectedException(typeof(ArgumentNullException))]
void ReplaceFirstChar0() {
this.ReplaceFirstChar(null, 0);
}
...
[TestMethod]
void ReplaceFirstChar10() {
this.ReplaceFirstChar("a", 'c');
}
泛型参数化单元测试
参数化单元测试可以是泛型方法。 在这种情况下,用户必须使用 PexGenericArguments 指定用于实例化方法的类型。
[PexClass]
public partial class ListTest {
[PexMethod]
[PexGenericArguments(typeof(int))]
[PexGenericArguments(typeof(object))]
public void AddItem<T>(List<T> list, T value)
{ ... }
}
允许例外
IntelliTest 提供了许多验证属性,可帮助将异常分类为预期的异常和意外异常。
预期异常通过使用相应的注释(如 ExpectedException(typeof(xxx))生成负面测试用例,而意外异常则生成失败的测试用例。
[PexMethod, PexAllowedException(typeof(ArgumentNullException))]
void SomeTest() {...}
验证程序为:
- PexAllowedException:允许任何位置的特定异常类型
- PexAllowedExceptionFromAssembly:允许来自指定程序集的特定异常类型
- PexAllowedExceptionFromType:允许来自指定类型的特定异常类型
- PexAllowedExceptionFromTypeUnderTest:允许被测试类型中的某个特定异常类型
测试内部类型
只要 IntelliTest 可以看到它们,IntelliTest 就可以“测试”内部类型。 要使 IntelliTest 查看类型,Visual Studio IntelliTest 向导会将以下属性添加到产品或测试项目中:
[assembly: InternalsVisibleTo("Microsoft.Pex, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")]
假设和断言
用户可以使用假设和断言来表达其测试 的先决条件 (假设)和 后置条件 (断言)。 当 IntelliTest 生成一组参数值并“浏览”代码时,它可能会违反测试的假设。 发生这种情况时,它不会为该路径生成测试,但会以无提示方式忽略它。
断言是常规单元测试框架中的已知概念,因此 IntelliTest 已经“了解”每个受支持的测试框架提供的内置 Assert 类。 但是,大多数框架不提供 Assume 类。 在这种情况下,IntelliTest 提供 PexAssume 类。 如果不想使用现有的测试框架,IntelliTest 还具有 PexAssert 类。
[PexMethod]
public void Test1(object o) {
// precondition: o should not be null
PexAssume.IsNotNull(o);
...
}
具体而言,非 null 性假设可以编码为自定义属性:
[PexMethod]
public void Test2([PexAssumeNotNull] object o)
// precondition: o should not be null
{
...
}
前提
方法的前提条件表示该方法成功的条件。
通常,前提条件是通过检查参数和对象状态来强制执行的,如果违反了参数,则会引发 ArgumentException 或 InvalidOperationException 。
在 IntelliTest 中, 参数化单元测试 的前置条件用 PexAssume 表示。
后置条件
方法的后置条件表示应在方法执行期间和之后保留的条件,前提是该方法的前提条件最初有效。
通常,后置条件是由对 Assert 方法的调用强制执行的。
使用 IntelliTest 时, 参数化单元测试 的后置条件用 PexAssert 表示。
测试失败
生成的测试用例何时失败?
如果未在 配置的路径边界内终止,则它被视为失败,除非设置了 TestExcludePathBoundsExceeded 选项
如果测试引发 PexAssumeFailedException,则成功。 但是,它通常被筛选掉,除非 TestEmissionFilter 设置为“全部”
如果测试违反断言;例如,由于单元测试框架引发了断言违反异常,测试将失败。
如果上述任何内容都没有生成决策,则仅当测试不引发异常时,测试才会成功。 断言违规的处理方式与异常相同。
安装和拆卸
作为与测试框架的集成的一部分,IntelliTest 支持检测和运行安装程序并拆解方法。
示例
using Microsoft.Pex.Framework;
using NUnit.Framework;
namespace MyTests
{
[PexClass]
[TestFixture]
public partial class MyTestClass
{
[SetUp]
public void Init()
{
// monitored
}
[PexMethod]
public void MyTest(int i)
{
}
[TearDown]
public void Dispose()
{
// monitored
}
}
}
延伸阅读
有反馈吗?
在 开发人员社区上发布想法和功能请求。