65.9K
CodeProject 正在变化。 阅读更多。
Home

Test Explorer

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.39/5 (9投票s)

2013年11月11日

CPOL

6分钟阅读

viewsIcon

51965

downloadIcon

970

用于运行 Visual Studio 测试类的测试自动化工具。

目录

引言

测试自动化是软件开发周期中的一个重要部分。能够运行这些测试并自动化运行这些测试的任务同样重要。感谢 Visual Studio,在 .NET Framework 上编写测试自动化一直是一项轻松的任务。但是,要运行一组测试用例,我们必须首先加载 Visual Studio,然后选择并运行测试用例。当我们想从不同的程序集和不同的项目中运行一组测试时,这项任务变得更加复杂,需要打开并运行多个 Visual Studio 实例,每个不同的测试项目一个。

基于这种需求,我构思了测试浏览器。测试浏览器是一个解决方案,它将扫描一个目录以查找所有包含测试类和测试方法的程序集。然后,它将构建一个包含所有已找到的至少一个测试方法的程序集的树,并添加诸如优先级和所有者之类的过滤选项。用户可以从不同的程序集/项目中选择一组测试,并安排 MSTest 运行这些测试。结果将保存为用户指定的用于结果文件夹的 trx 文件,并且可以选择将这些结果作为 trx 文件或 HTML 通过电子邮件发送。

Test Explorer

此应用程序将扫描预定义文件夹并查找所有包含测试类的程序集。在我的团队中,我们已定义所有测试项目(测试程序集)都以关键字“testmodule”命名,从而简化了扫描过程。

string dropFolder = "drop\\Debug"; //Folder that contains the test assemblies 
string testFolder = "drop\\TestResults"; //Folder to place the test results

对于在放置文件夹中找到的每个程序集,让我们对其进行扫描并检索所有测试方法。我们感兴趣的、可用于过滤的属性是:“OwnerAttribute”、“PriorityAttribute”。

public TreeNode GetTestMethods(string AssambleyPath)
{
    TreeNode _node = new TreeNode();
    _node.Text = Path.GetFileNameWithoutExtension(AssambleyPath);
    
    Assembly _assembly = Assembly.LoadFile(AssambleyPath);
    Type[] types = _assembly.GetExportedTypes();
    foreach (Type type in types)
    {
        Attribute _classAttribute  = type.GetCustomAttribute(typeof(TestClassAttribute));
        if(_classAttribute != null)
        {
            TreeNode _n = new TreeNode();
            _n.Text = type.Name;
            MemberInfo[] members = type.GetMembers(BindingFlags.Public| 
              BindingFlags.Instance| BindingFlags.InvokeMethod);
            foreach (MemberInfo member in members)
            {                        
                Attribute _att = member.GetCustomAttribute(typeof(TestMethodAttribute));
                if (_att != null)
                {
                    TestMethod _t = new TestMethod(member.Name);
                    foreach (var t in member.CustomAttributes)
                    {
                        if (t.AttributeType.Name == "OwnerAttribute")
                            {
                            _t.Owner = t.ConstructorArguments[0].Value.ToString();
                        }
                        else if (t.AttributeType.Name == "PriorityAttribute")
                        {
                            _t.Priority = 
                             Int32.Parse(t.ConstructorArguments[0].Value.ToString());
                        }
                    }
                    //Adding owner to the owner lists
                    if (!ownerCheckboxList.Items.Contains(_t.Owner))
                    {
                        ownerCheckboxList.Items.Add(_t.Owner);
                    }
                    //Adding priority to the priority lists
                    if (!priorityCheckBoxList.Items.Contains
                                  (string.Format("P{0}",_t.Priority)))
                    {
                        priorityCheckBoxList.Items.Add(string.Format("P{0}",_t.Priority));
                    }
 
                    string key = string.Format("K{0}", TotalCountOfTests++);
 
                    _TestMethodLists.Add(key, _t);
                    _n.Nodes.Add(key, member.Name);
                }                    
            }
            _node.Nodes.Add(_n);
        }
    }
    return _node;
}

构建命令

一旦加载了所有测试方法并显示了适当的过滤器(优先级和所有者),用户就可以选择要运行的来自不同程序集的任意数量的测试。如果至少选择了一个程序集中的测试方法,则会构建一个 MSTest 命令。如果您不知道,MSTest.exe 是 Visual Studio 附带的命令行工具,用于运行测试。您可以在此处找到有关运行 MSTEST 的命令行参数的更多信息。

用于运行测试的命令将包含

  • 程序集路径,用于 testcontainer 选项
  • 结果文件路径,用于 resultsfile 选项
  • 该程序集选定测试方法的名称列表,用于 detail:testname 选项

结果路径

设置选项

作为可选参数,您可以指定一个 testsetting 文件来运行测试方法。

作为示例,我提供了一个通用的测试设置文件,该文件为每个测试用例指定了 timeout 属性(下面代码中为 30000 毫秒)

<?xml version="1.0" encoding="UTF-8"?>
<TestSettings name="TestSettings1" id="29046417-d7da-4b0a-a412-d4ede2bc9050" 
      xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
  <Description>These are default test settings for a local test run.</Description>
  <Execution>
    <Timeouts testTimeout="300000" />    
    <AgentRule name="LocalMachineDefaultRole">
    </AgentRule>
  </Execution>
</TestSettings>

进程包装器

为了执行测试方法,我将调用 MSTest。Visual Studio 附带了一组命令行工具,在尝试自动化进程时非常方便。

进程 Wrapper 类只是一个包装器类,它将使用 System.Diagnostics Process 调用 MSTest

第一步是获取 MSTest 路径。在我的例子中,由于我安装了 Visual Studio 2012,这将是 VS110COMNTOOLS

vsStudioPath = Environment.GetEnvironmentVariable
               ("VS110COMNTOOLS", EnvironmentVariableTarget.Machine);
if (vsStudioPath.EndsWith("\\"))
{
    vsStudioPath = vsStudioPath.Substring(0, vsStudioPath.LastIndexOf('\\'));
}
vsStudioPath = vsStudioPath.Substring(0, vsStudioPath.LastIndexOf('\\'));
_MsTestPath = vsStudioPath + "\\IDE\\MSTest.exe";

下一步是检索进程并重定向标准输出和标准错误,以便将其显示给用户作为运行测试的日志结果。

p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.FileName = _MsTestPath;
p.StartInfo.Arguments = Command;

最后,一旦进程结束,获取标准输出

_StdOutput = p.StandardOutput.ReadToEnd();
_StdErr = p.StandardError.ReadToEnd();

测试方法类

此类旨在保存我们在此程序集中找到的每个测试方法的重要数据。在此快速演示中,我添加到 Test Method 对象中的唯一数据是所有者、测试方法名称和优先级。我选择这些字段是基于我用来安排测试运行的常见查询/过滤器。但您可以随意添加任意数量的额外字段。

/// <summary>
/// Gets or Sets the Test Method Priority
/// </summary>
public int Priority
{
    get
    {
        return _Priority;
    }
    set
    {
        _Priority = value;
    }                
}
 
/// <summary>
/// Gets or Sets the Test Method Owner
/// </summary>
public string Owner
{
    get
    {
        return _Owner;
    }
    set
    {
        _Owner = value;
    }
}
 
/// <summary>
/// Gets the Test Method Name
/// </summary>
public string Name
{
    get
    {
        return _Name;
    }
}

将 TRX 文件转换为 HTML

另一个很酷的功能是能够使用名为 trx2html 的外部库将 Visual Studio 测试结果文件转换为 HTML(http://trx2html.codeplex.com/)。测试结果文件可以转换为带有汇总信息的 HTML,从而更轻松地检查测试结果。通过 HTML 格式的结果,我们可以轻松地将自动化报告通过 HTML 正文发送电子邮件。作为第二个选项,我们仍然可以将结果作为附件发送,使用传统的 trx 文件发送电子邮件。

trx2html.ReportGenerator _Parser = new trx2html.ReportGenerator();
foreach (string _t in _ResultsFilePaths)
{
    _Parser.GenerateReport(_t);
    _progress.UpdateProgressBar();
    HtmlReportFilesPath.Add(_t + ".htm");
}

发送电子邮件报告

发送带有测试通过结果的电子邮件

此类允许您自动将测试通过结果发送给用户列表(例如:测试人员、团队负责人等),以便他们可以针对失败的测试用例采取行动。

使用 C# SmtpClientMailMessage 发送电子邮件,收件人列表将收到测试结果报告作为文件附件(Visual Studio 生成的 TRX 文件),或者如果“转换为 HTML”选项在测试运行前被选中,则以 HTML 格式发送。

为客户端设置正确的 SMTP 电子邮件服务器的端口和主机,并提供有效的网络凭据非常重要。

MailMessage msg = new MailMessage();
SmtpClient client = new SmtpClient();
client.Port = smtpPort;        
client.Host = smtpHost;
client.EnableSsl = smtpSsl;
client.Credentials = new NetworkCredential(userName,password);

添加发件人详细信息

msg.From = new MailAddress(_From, _displayName, System.Text.Encoding.UTF8);

添加收件人列表

msg.To.Add(emailTo);

附加文件的示例方法

public void AddAttachment(string fileName)
{
     //Agrego el archivo que puse en la ruta anterior "PathFile", y su tipo.
    Attachment Data = new Attachment(fileName, MediaTypeNames.Application.Octet);
 
    //Obtengo las propiedades del archivo.
    ContentDisposition disposition = Data.ContentDisposition;
    disposition.CreationDate = System.IO.File.GetCreationTime(fileName);
    disposition.ModificationDate = System.IO.File.GetLastWriteTime(fileName);
    disposition.ReadDate = System.IO.File.GetLastAccessTime(fileName);
    //Agrego el archivo al mensaje
    msg.Attachments.Add(Data);
}

增强功能

作为可选用法,我们可以创建一个文本文件中的分发列表,其中包含有效电子邮件收件人地址的列表。在我的示例中,我创建了一个名为 EmailDL.cfg 的文件。程序将加载收件人列表并将电子邮件发送给他们。这样,修改/添加电子邮件地址就更容易了。

清理目录

作为测试运行的一部分,MSTest 会创建部署文件夹以保存运行测试所需的二进制文件和程序集。作为清理过程的一部分,我想删除这些文件夹。如果您不想删除部署文件夹,可以简单地跳过对 CleanUpDirectories() 的调用。

/// <summary>
/// Cleans up all folder directories in the TestResults folder
/// </summary>
private void CleanUpDirectories()
{            
    string[] filePaths = Directory.GetDirectories(_ResultsPath);
 
    foreach (string folder in filePaths)
    {
        CleanDirectory(new DirectoryInfo(folder));
        Directory.Delete(folder);
    }
}

/// <summary>
/// Cleans a single directory content
/// </summary>
/// <param name="directory"></param>
private void CleanDirectory(DirectoryInfo directory)
{
    foreach (FileInfo file in directory.GetFiles())
    {
        file.Delete();
    }
    foreach (DirectoryInfo subDirectory in directory.GetDirectories())
    {
        subDirectory.Delete(true);
    }
}

结论

这个项目被证明是相当有趣和令人愉快的。我认为这个项目产生的应用程序非常有用,可以在不需要打开 Visual Studio 的情况下运行测试。仍然有许多增强功能和待办事项我想添加以提高此应用程序的实用性。在这些主要功能中,我想在项目的未来迭代中添加

  • 异步/多线程支持,以便显示带有测试整体状态、当前运行测试、剩余测试的进度条,以及取消按钮以中止当前测试运行。
  • 任务调度支持:这将是一个非常好的功能。使用 Windows 任务调度程序功能,我们可以提前安排多次测试运行,以及周期性测试运行。在我的团队中,我们目前每天午夜运行整个测试套件。在应用程序中选择和安排具有选定测试用例的测试运行的选项将大大提高其可用性。
  • 应用程序内的图表和摘要报告。拥有所有报告、TRX 文件和 HTML 文件很棒;但有一个显示成功/失败率的图表,以及一个显示失败/通过测试用例的简要摘要也会很棒。

历史

  • 修订 0 - 新文章
  • 修订 1 - 修正了排版和语法错误
© . All rights reserved.