TFS、自动化测试和持续集成






4.95/5 (11投票s)
TFS 中的自动化测试和持续集成
引言
TFSBuild 是一个可高度定制的工具。许多人可能已经在使用它来构建应用程序并将其部署到测试环境。这些环境可能是虚拟环境、实验室环境或云端环境。本文我想讨论的是如何使用 TFS 自动测试我们的测试环境。也就是说,定义一组“部署后”测试,这些测试可以在最新版本的代码库部署到测试环境后对其运行。这些测试可能只是检查所有组件是否已正确部署并可用,或者它们实际上可能尝试执行系统功能和端到端操作。
这是持续集成的最佳实践。我可以每天,甚至每小时,获得关于系统最新版本运行情况的反馈。在开发周期结束时不会有令人讨厌的意外,我们可以将一个环境交给测试人员,并且知道它符合要求。
如何从 TFSBuild 部署应用程序不在本文的讨论范围之内,并且很大程度上取决于您要部署什么以及要部署到哪里。我将讨论的是如何自定义 TFS Build,以便您可以运行一组“部署后”测试,并讨论这些测试可能是什么样子以及您可能面临的其他问题。
提供了使用 VS 2010 针对 TFS 2010 编写的示例代码,尽管完全相同的原理适用于 TFS 2012。
背景
我使用 MSTest 来运行我的部署后测试,但理论上您可以使用其他测试框架(TFS 2012 中对其他测试框架有增强支持)。有些纯粹主义者会说,启动服务或测试网站的部署后测试远远超出了单元测试的范围,因此不应使用 MSTest 运行。
他们是对的,这些测试确实远远超出了单元测试的范围,但我将 MSTest 视为一个测试框架,而不是一个单元测试框架,而且 MSTest 中提供的许多功能在应用程序级别进行测试时都能完美运行。
这里的许多自定义都涉及编辑驱动 TFSBuild 的 Windows Workflow。如果您不熟悉自定义 TFSBuild 或 Windows Workflow,那么这篇文章是一个很好的介绍。
运行测试
标准的 TFS 构建模板已经允许您定义一组单元测试在构建后运行。我们要做的是自定义构建模板,以允许我们定义第二组单元测试在部署后运行。为此,我们可以重用许多已有的东西,并且这样做可以保留我们当前用于运行单元测试的所有功能,例如测试类别和测试设置文件。
步骤 1) 定义部署后测试
TFS 巧妙地提供了一个类型 TestSpecList,用于存储单元测试信息。我们将向工作流参数列表添加一个此类型的新参数 TestSpecsPostDeployment
。我们还添加了一个布尔值,以允许我们启用或禁用这些测试的运行,DisablePostDeploymentTests
。
TestSpecsPostDeployment
的编辑器设置为使用类型 Microsoft.TeamFoundation.Build.Controls.TestSpecListEditor
。这与我们用于编辑构建中标准单元测试的编辑器相同,因此允许我们使用完全相同的功能和特性,例如测试类别和测试设置文件。步骤 2) 运行测试
“在代理上运行”序列将类似于这样,您可以看到构建后发生部署,然后是我们为部署后测试创建的部分。
TestSpecList
的逻辑,因为这就是运行标准单元测试的逻辑。如果您深入到“尝试编译、测试和关联变更集和工作项”序列并继续,您会找到“运行测试”序列。如果您找不到它,可以尝试搜索 XAML。我们将复制此工作流并将其粘贴到“部署后”序列中。- 将所有对
TestSpecs
的引用替换为TestSpecsPostDeployment
- 将msbuild活动中对
platformConfiguration
的任何引用替换为BuildSettings.PlatformConfigurations.First()
- 将工作流变量
outputDirectory
和treatTestFailureAsBuildFailure
的作用域更改为“在代理上运行”级别。直接编辑 XAML 可能更容易。
最后,“尝试编译和测试”序列的末尾有一些逻辑来处理“将测试失败视为构建失败逻辑”。这需要复制到部署后测试序列的末尾,最终我们得到类似这样的结果。
针对环境
这里可能会出现一个问题:“我维护多个测试和开发环境,如何控制我的测试针对哪个环境运行?”您可能正在作为构建的一部分启动一个虚拟实验室,在这种情况下,要测试的环境每次都会不同。
这基本上涉及向您的测试传递一些数据,让它知道要针对哪个环境运行(另一个单元测试的大忌,但对于我们这种情况来说没问题)。有许多可能的解决方案,但我选择的解决方案是让测试从 app.config 读取这些数据,并让 TFSBuild 在使用自定义构建活动运行测试之前更新此 config 文件。
为此,我在构建模板中添加了另一个参数 TestEnvironment
,并编写了一个自定义构建活动,可以更新测试项目 config 文件中的 appsetting
。如果您没有创建自定义构建活动的经验,我建议您参考这篇优秀的文章。
此自定义构建活动的代码如下所示
BuildActivity(HostEnvironmentOption.Agent)]
public sealed class UpdateAppSetting : CodeActivity
{
[RequiredArgument]
public InArgument<string> FileName { get; set; }
[RequiredArgument]
public InArgument<string> Name { get; set; }
[RequiredArgument]
public InArgument<string> Value { get; set; }
protected override void Execute(CodeActivityContext context)
{
string name = context.GetValue(Name);
string fileName = context.GetValue(FileName);
string value = context.GetValue(Value);
UpdateConfigFileAppSetting(fileName, name, value);
}
/// <summary>
/// Update an app setting in an app config file. If it is not present then add it.
/// </summary>
/// <param name="configFile"></param>
/// <param name="name"></param>
/// <param name="value"></param>
private void UpdateConfigFileAppSetting(string configFile, string name, string value)
{
MakeWritable(configFile);
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = configFile;
Configuration config =
ConfigurationManager.OpenMappedExeConfiguration
(fileMap, ConfigurationUserLevel.None);
if (!config.AppSettings.Settings.AllKeys.Contains(name))
config.AppSettings.Settings.Add
(new KeyValueConfigurationElement(name, string.Empty));
config.AppSettings.Settings[name].Value = value;
config.Save(ConfigurationSaveMode.Modified);
}
/// <summary>
/// Make a file writeable
/// </summary>
/// <param name="file"></param>
private void MakeWritable(string file)
{
FileInfo fileInfo = new System.IO.FileInfo(file);
if (fileInfo.IsReadOnly)
fileInfo.IsReadOnly = false;
}
}
然后更新构建工作流以调用此自定义活动,并在运行部署后测试之前更新测试 DLL 的 config 文件。请注意,我们更新 binaries 文件夹中的 config 文件,因为测试是从此文件夹运行的。
部署后测试的建议
这个列表真是无穷无尽,无论您在 .NET 中能做什么,都可以封装到一个测试中。这里有一些我使用的:
- 使用 ServiceController 类检查您部署的任何 Windows 服务是否可用并已启动
- 测试与您正在部署的任何数据库的连接
- 向您的网站主页或 Web 服务端点发送 HTTP 请求,以确保它们正在响应
- 执行 Web 服务上的方法并测试结果
- 使用像 Selenium 这样的工具来锻炼您网站的功能
为了给您一个概念,这里有一个非常简单的部署后冒烟测试的示例,它测试一个 Windows 服务是否正在运行:
TestClass]
public class WindowsServiceTest
{
[TestMethod]
[TestCategory("PostDeployment")]
public void TestSimpleWindowsService()
{
// This value is updated by the build workflow
string hostName = ConfigurationManager.AppSettings["TestEnvironment"];
TestWindowsService("SimpleWindowsService", hostName);
}
private void TestWindowsService(string serviceName, string hostName)
{
using (ServiceController sc = new ServiceController(serviceName, hostName))
{
// Ensure the service is running
if (!(sc.Status == ServiceControllerStatus.Running))
{
WindowsServiceHelper.StartService(serviceName, hostName);
// This will wait up to a minute for the service to be started
bool isRunning = WindowsServiceHelper.PollServiceStatus(serviceName, hostName,
60, 1000, ServiceControllerStatus.Running);
if(!isRunning)
throw new AssertFailedException(
string.Format("Service {0} is not running and cannot be started", serviceName));
}
}
}
}
总结
如果您已经读到这里,那么我希望这篇文章为您提供了许多关于持续集成和自动化测试以及如何使用 TFS 创建一个真正出色的持续集成解决方案的想法。