本主题演示如何使用支持测试优先开发的 “从使用情况生成 ”功能。
测试优先开发 是软件设计的一种方法,你首先基于产品规范编写单元测试,然后编写使测试成功所需的源代码。 在测试用例中首次引用新类型和成员时,Visual Studio 支持先在源代码中生成新类型和成员,然后再对其进行定义,从而支持测试优先开发。
Visual Studio 会生成新类型和成员,同时对您的工作流几乎没有干扰。 可以为类型、方法、属性、字段或构造函数创建存根,而无需在代码中保留当前位置。 打开对话框以指定类型生成选项时,当对话框关闭时,焦点将立即返回到当前打开的文件。
Generate From Usage 功能可用于与 Visual Studio 集成的测试框架。 本主题演示了Microsoft单元测试框架。
注释
本文中的说明阐述了 Visual Studio 中可用的最新版本的交互式开发体验 (IDE)。 你的计算机可能会对某些用户界面元素显示不同的名称或位置。 你可能正在使用其他版本的 Visual Studio 或其他环境设置。 有关详细信息,请参阅个性化设置 IDE。
创建 Windows 类库项目和测试项目
在 C# 或 Visual Basic 中,创建新的 Windows 类库 项目。 命名它
GFUDemo_VB
,或者GFUDemo_CS
,具体取决于所使用的语言。在 解决方案资源管理器中,右键单击顶部的解决方案图标,然后选择“ 添加新>项目”。
创建新的 单元测试项目 (.NET Framework) 项目。
添加对类库项目的引用
在 解决方案资源管理器的单元测试项目下,右键单击 “引用 ”条目,然后选择“ 添加引用”。
在 “引用管理器 ”对话框中,选择 “项目 ”,然后选择类库项目。
选择 “确定 ”关闭 “引用管理器 ”对话框。
保存解决方案。 现在,就可以开始编写测试。
从单元测试生成新类
测试项目包含名为 UnitTest1 的文件。 在 解决方案资源管理器 中双击此文件,在代码编辑器中将其打开。 已生成测试类和测试方法。
找到类
UnitTest1
的声明并将其重命名为AutomobileTest
。注释
IntelliSense 现在为 IntelliSense 语句完成提供两种替代方法: 完成模式 和建议 模式。 对于先使用类和成员然后再对其进行定义的情况,采用建议模式。 当 IntelliSense 窗口打开时,可以 按 Ctrl+Alt+空格 在完成模式与建议模式之间切换。 有关详细信息,请参阅 “使用 IntelliSense ”。 在下一步中键入
Automobile
内容时,建议模式将有所帮助。找到
TestMethod1()
方法并将其重命名为DefaultAutomobileIsInitializedCorrectly()
。 在此方法中,创建名为Automobile
的类的新实例,如以下屏幕截图所示。 将出现一条波浪形下划线,指示编译时错误,且快速操作错误灯泡会出现在左边距中,或直接出现在波浪线下(如果将鼠标悬停在波浪线上)。选择或单击“快速操作”灯泡。 你将看到一条错误消息,指出类型
Automobile
未定义。 你还会看到一些解决方案。单击“ 生成新类型 ”打开“ 生成类型 ”对话框。 此对话框提供的选项包括在不同的项目中生成类型。
在 “项目 ”列表中,单击 GFUDemo_VB 或 GFUDemo_CS ,指示 Visual Studio 将文件添加到类库项目而不是测试项目。 如果尚未选择该文件,请选择“ 创建新文件 ”并将其命名 为Automobile.cs 或 Automobile.vb。
单击“ 确定” 关闭对话框并创建新文件。
在 解决方案资源管理器中,查看 GFUDemo_VB 或 GFUDemo_CS 项目节点,以验证新 Automobile.vb 或 Automobile.cs 文件是否存在。 在代码编辑器中,焦点仍在
AutomobileTest.DefaultAutomobileIsInitializedCorrectly
,这使你能够以最少的中断继续编写测试。
生成属性存根
假设产品规范指出该 Automobile
类具有两个命名 Model
的公共属性和 TopSpeed
。 必须使用默认值 "Not specified"
和 -1
默认构造函数初始化这些属性。 以下单元测试将验证默认构造函数是否将属性设置为其正确的默认值。
将以下代码行添加到
DefaultAutomobileIsInitializedCorrectly
测试方法。由于代码引用了
Automobile
上的两个未定义的属性,因此,波浪下划线会出现在Model
和TopSpeed
下方。 将鼠标悬停在Model
上,并选择 “快速操作” 错误灯泡,然后选择 “生成属性‘Automobile.Model’”。以相同的方式为
TopSpeed
属性生成属性存根。在类中
Automobile
,从上下文中正确推断出新属性的类型。
为新的构造函数生成存根
现在,我们将创建一个测试方法,该方法将生成一个用于初始化Model
和TopSpeed
属性的构造函数存根。 稍后,你将添加更多代码以完成测试。
将以下附加测试方法添加到
AutomobileTest
类。单击红色波形线下的“快速操作”错误灯泡,然后单击“在 'Automobile' 中生成构造函数”。
在
Automobile
类文件中,请注意,新构造函数已检查构造函数调用中使用的局部变量的名称、找到类中Automobile
具有相同名称的属性,并在构造函数正文中提供代码来存储参数值Model
和TopSpeed
属性。生成新构造函数后,在对默认构造函数
DefaultAutomobileIsInitializedCorrectly
的调用下显示波浪下划线。 错误消息指出该Automobile
类没有采用零参数的构造函数。 若要生成没有参数的显式默认构造函数,请单击 “快速作 ”错误灯泡,然后单击 “汽车”中的“生成构造函数”。
为方法生成存根
假定规范指出,如果将新Automobile
的Model
属性和TopSpeed
属性设置为非默认值,则可以将其放入IsRunning
状态。
将以下行添加到
AutomobileWithModelNameCanStart
方法。单击
myAuto.Start
方法调用的“快速操作”错误灯泡,然后单击“生成方法 'Automobile.Start'”。单击快速操作灯泡以获取
IsRunning
属性,然后单击生成属性 'Automobile.IsRunning'。该
Automobile
类现在包含一个名为Start()
的方法和一个名为IsRunning
的属性。
运行测试
在 “测试 ”菜单上,选择“ 运行>所有测试”。
“运行>所有测试”命令运行为当前解决方案编写的任何测试框架中的所有测试。 在这种情况下,有两个测试,它们都按预期失败。 测试
DefaultAutomobileIsInitializedCorrectly
失败,因为Assert.IsTrue
条件返回False
。 测试AutomobileWithModelNameCanStart
失败,因为Start
类中的Automobile
方法会引发异常。下图显示了“ 测试结果 ”窗口。
在“ 测试结果 ”窗口中,双击每个测试结果行,转到每个测试的位置。
实现源代码
将以下代码添加到默认构造函数,以便
Model
TopSpeed
IsRunning
所有属性都初始化为其正确的默认值"Not specified"
(-1
False
或false
C# )。Start
方法调用时,仅在Model
或TopSpeed
属性被设置为非默认值时,才应该将IsRunning
标志设置为 true。NotImplementedException
从方法正文中删除并添加以下代码。
再次运行测试
在 “测试 ”菜单上,指向 “运行”,然后单击“ 所有测试”。
这次测试通过了。 下图显示了“ 测试结果 ”窗口。