测试生成

在传统的单元测试中,测试由以下几个方面组成:

下面是一个示例测试结构:

[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() {...}

验证程序为:

测试内部类型

只要 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
{
   ...
}

前提

方法的前提条件表示该方法成功的条件。

通常,前提条件是通过检查参数和对象状态来强制执行的,如果违反了参数,则会引发 ArgumentExceptionInvalidOperationException

在 IntelliTest 中, 参数化单元测试 的前置条件用 PexAssume 表示。

后置条件

方法的后置条件表示应在方法执行期间和之后保留的条件,前提是该方法的前提条件最初有效。

通常,后置条件是由对 Assert 方法的调用强制执行的。

使用 IntelliTest 时, 参数化单元测试 的后置条件用 PexAssert 表示。

测试失败

生成的测试用例何时失败?

  1. 如果未在 配置的路径边界内终止,则它被视为失败,除非设置了 TestExcludePathBoundsExceeded 选项

  2. 如果测试引发 PexAssumeFailedException,则成功。 但是,它通常被筛选掉,除非 TestEmissionFilter 设置为“全部

  3. 如果测试违反断言;例如,由于单元测试框架引发了断言违反异常,测试将失败。

如果上述任何内容都没有生成决策,则仅当测试不引发异常时,测试才会成功。 断言违规的处理方式与异常相同。

安装和拆卸

作为与测试框架的集成的一部分,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
        }
    }
}

延伸阅读

有反馈吗?

开发人员社区上发布想法和功能请求。