我将发布我为使 SpecFlow 测试结果显示在 Azure DevOps Builds->Tests 区域中所做的工作,但请注意,这是一个giant hack。
我相信 SpecFlow/NUnit 测试应该像常规 NUnit 测试一样得到支持,而且我确实认为它与 Andreas Willich 所说的测试适配器配置有关,但是我都无法获得它对我有用,我也找不到任何人通过正常管道配置使其工作的示例。 SpecFlow+ 可能也有工作方式,但我不使用 plus 版本。如果/当我能学会正确的方法来做到这一点时,我将停止使用下面的方法。
创建一个简单的过程,以 CSV 格式将 SpecFlow 测试名称和测试结果值写入光盘。我这样做是因为我想将功能测试解决方案与这个 hack 发生的任何其他逻辑分开。这个过程应该很简单,就像使用 [AfterScenario] 挂钩步骤从 Scenario.Context 中提取场景标题和结果值 - 或者您可以获取它们的任何地方 - 并将它们以逗号分隔值格式写入文本文件。
将该 CSV 结果记录过程集成到“SpecFlow”项目中,该项目包含您希望其结果显示在 Azure DevOps 中的测试。
CSV 应该如下所示:
DemoScenario_01 Lorem ipsum dolor sit amet consectetur adipiscing elit, Pass
DemoScenario_02 Sed do eiusmod tempor incididunt ut labore et dolore, Pass
DemoScenario_03 Magna aliqua Ut enim ad minim veniam quis, Pass
DemoScenario_04 Nostrud exercitation ullamco laboris nisi ut aliquip, Pass
DemoScenario_05 Ex ea commodo consequat Duis aute irure dolor in, Pass
DemoScenario_06 Reprehenderit in voluptate velit esse cillum dolore eu, Pass
DemoScenario_07 Fugiat nulla pariatur Excepteur sint occaecat cupidatat, Pass
DemoScenario_08 Non proident sunt in culpa qui officia semper, Pass
DemoScenario_09 Deserunt mollit anim id est laborum arcu semper, Pass
DemoScenario_10 Orci a scelerisque purus semper eget Ornare arcu dui vivamus, Pass
- 创建一个单独的简单“虚拟”项目,其中仅包含一个类,该类具有一个工作 NUnit“测试”方法,除了 Assert.Pass() 之外什么都不做。此项目需要安装 NUnit 和 NUnit3TestAdapter NuGet 包。
这个类应该是这样的:
namespace DemoNunit
{
public class Tests
{
[Test]
public static void DemoTest005()
{
Assert.Pass();
}
}
}
为此“虚拟”项目的解决方案创建一个 Azure DevOps Git 代码存储库并将其推送到存储库。
创建一个为 CI 构建配置的新 Azure 管道,当“虚拟”NUnit 项目的提交被推送到其存储库时,该管道将自动触发。将管道配置为具有一个名为“SpecFlow Tests”的 Visual Studio 测试步骤,该步骤会查找包含该单个 NUnit 测试的 dll。根据您的“SpecFlow”项目或其测试的功能命名此管道,因为此管道实际上最终会在 Builds->Tests 区域中显示这些结果。
在一个新的单独的“convert”项目中,创建一个进程,该进程将从“SpecFlow”项目中读取简单的 CSV 输出结果文件,并在“ dummy”项目,包含 NUnit 测试。
“虚拟”项目中的单个 NUnit 测试方法现在将替换为多种方法,一种用于记录在 CSV 中的每个结果。这些方法将被命名为 guid 减去任何破折号,并以一个字母作为前缀,以使它们成为有效的 C# 方法名称。目标只是拥有不重复的方法名称。实际的 SpecFlow 测试场景名称将存储在 NUnit TestCase TestName 属性中。 Assert.Pass() 或 Assert.Fail() 将根据从 CSV 文件中读取的相关结果值使用。将此“转换”项目编译为 exe。
我省略了读取 CSV 结果的代码...
namespace CreateCsFile
{
public static class CsFile
{
public static string OpenClass =
"using NUnit.Framework;" +
"namespace DemoNunit" +
"{" +
" public class Tests" +
" {";
public static string CloseClass =
" }" +
"}";
public static string TestMethod =
" [Test, TestCase(TestName = \"UniqueNameAttribute\")]" +
" public static void MethodName()" +
" {" +
" Assert.Result();" +
" }";
public static void LogListOfResults(
List<Test> resultsList)
{
Log.CsharpFile(OpenClass);
foreach (var result in resultsList)
{
Outcome.IsValid(result.Result);
var testMethod =
TestMethod.Replace(
"UniqueNameAttribute",
result.Name).
Replace(
"Result",
result.Result).
Replace("MethodName",
"a" + Guid.NewGuid().
ToString().
Replace("-",""));
Log.CsharpFile(testMethod);
}
Log.CsharpFile(CloseClass);
}
public static void ConvertCsvResultsToNunitResults()
{
LogListOfResults(
ParseCsv.ResultsSheet());
}
}
}
日志类...
namespace CreateCsFile
{
public class Log
{
public static string WasFileRemoved
= "";
public static string CsFileToggle
= "True";
public static string CsFile
= "C:\\Projects\\DemoNunit\\Tests.cs";
public static void CsharpFile(
string nunitData)
{
if (CsFileToggle.ToUpper()
== "TRUE")
{
if (string.IsNullOrEmpty(
WasFileRemoved))
{
RemoveExistingCsFile();
}
var log = !File.Exists(CsFile) ?
new StreamWriter(CsFile) :
File.AppendText(CsFile);
log.WriteLine(nunitData);
log.Close();
}
}
public static void RemoveExistingCsFile()
{
if (CsFileToggle.ToUpper()
== "TRUE")
{
WasFileRemoved = "True";
try
{
var fileInfo =
new FileInfo(CsFile);
fileInfo.Attributes =
FileAttributes.Normal;
File.Delete(fileInfo.FullName);
}
catch
{
throw new Exception(
@"Unable to delete existing csharp file...");
}
}
}
}
}
丑陋的输出:
namespace DemoNunit{ public class Tests {
[Test, TestCase(TestName = "DemoScenario_01 Lorem ipsum dolor sit amet consectetur adipiscing elit")] public static void aa5f7fd239d6a40878780bc6c81f3a18b() { Assert. Pass(); }
[Test, TestCase(TestName = "DemoScenario_02 Sed do eiusmod tempor incididunt ut labore et dolore")] public static void aa9882fa95b17499eb9386b20a7ff303d() { Assert. Pass(); }
[Test, TestCase(TestName = "DemoScenario_03 Magna aliqua Ut enim ad minim veniam quis")] public static void a440c25f8c3c24e92ad90224da56bafda() { Assert. Pass(); }
[Test, TestCase(TestName = "DemoScenario_04 Nostrud exercitation ullamco laboris nisi ut aliquip")] public static void ab2c3cc6997df4a42b0992128f63358f7() { Assert. Pass(); }
[Test, TestCase(TestName = "DemoScenario_05 Ex ea commodo consequat Duis aute irure dolor in")] public static void a2c9f744dcd2c42c99cb6e288cf09fc78() { Assert. Pass(); }
[Test, TestCase(TestName = "DemoScenario_06 Reprehenderit in voluptate velit esse cillum dolore eu")] public static void a294422ae029049f9ac4be6f9bb4529cc() { Assert. Pass(); }
[Test, TestCase(TestName = "DemoScenario_07 Fugiat nulla pariatur Excepteur sint occaecat cupidatat")] public static void aa2dcdf889ffe4e46b57a73882d6f1a68() { Assert. Pass(); }
[Test, TestCase(TestName = "DemoScenario_08 Non proident sunt in culpa qui officia semper")] public static void a891c43376a5049f89ad75b70fa0a543f() { Assert. Pass(); }
[Test, TestCase(TestName = "DemoScenario_09 Deserunt mollit anim id est laborum arcu semper")] public static void aa8da317895214abc966c229c832c162f() { Assert. Pass(); }
[Test, TestCase(TestName = "DemoScenario_10 Orci a scelerisque purus semper eget Ornare arcu dui vivamus")] public static void aa9accb0c9c1b4918b76bc75ff2f2e835() { Assert. Pass(); }
}}
现在执行以下步骤:
- 使用命令行通过命令行执行“SpecFlow”项目的测试
NUnit 控制台 TestRunner(它应该被配置为删除 CSV
输出到某个地方,如 c:\Temp)
- 执行您的“转换”项目,该项目应配置为读取
c:\Temp 中的 CSV 并更新“虚拟”项目中的 .cs 文件
其克隆的 repo 存在于磁盘上的任何位置
- 通过命令行参数,有 git commit 并推送到 repo
将“转换”exe 更改为“虚拟”项目的 .cs 文件
Azure 管道现在将使用它存储在 CSV 中的名称和结果构建“SpecFlow”项目的测试执行结果并将其显示为“SpecFlow 测试”(我的也有一些演示 ID)
可以通过不同的方式进行设置;一种方法是:
- 将您的“SpecFlow”项目放入 git 存储库并创建 CI 管道
为它
- 添加构建步骤集以通过命令行执行测试
- 添加一个步骤来调用已放入
修复构建框上的位置并更新“虚拟”项目的文件,该文件位于共享位置的单独 VM 中
- 添加一个步骤,将“虚拟”项目的最新更新推送到 git 存储库
此管道现在将在提交推送到其存储库时自动触发,并且该过程将自动将 SpecFlow 结果转换为 NUnit 结果,触发辅助管道,并在该秒的 Builds->Tests 区域中显示 SpecFlow 结果管道。第一个管道不会显示任何结果,您总是会查看第二个管道来查看它们。
以这种方式支持更多/其他“SpecFlow”项目......他们可能都共享“转换”部分,但需要改进以能够将 . cs 文件正在更新。然后,您可以为您希望以这种方式查看结果的每个“SpecFlow”项目创建单独的“虚拟”项目(当然还有相关的存储库/管道)。我还没有走到这一步,因为我目前只为一个项目做这件事。