我正在尝试建立一个Azure DevOps项目,该项目在执行生成时会运行一些自动验收测试。
我在本地计算机上运行验收测试没有问题,但是当我在DevOps平台上执行构建时,似乎测试运行不正确。我的单元测试按预期运行,但是验收测试根本没有运行。这是TestAssemblies的完整输出日志:https://pastebin.com/ZCx1RdGp
编辑:删除Runner和SpecFlow程序包后,我得到以下输出:https://pastebin.com/tTtKEcX5
最受我欢迎的是下面的几行。似乎正在尝试运行测试,但并没有正确执行:
2019-01-14T12:22:38.9295662Z Profile: Acceptance
2019-01-14T12:22:38.9324097Z starting test run
2019-01-14T12:22:38.9324763Z Discovering target: Default
2019-01-14T12:22:39.0023466Z test run finished
2019-01-14T12:22:39.0025670Z publishing test results
2019-01-14T12:22:39.0026124Z test results published
2019-01-14T12:22:39.0026476Z generating reports
2019-01-14T12:22:39.0026874Z creating 1 report(s)
2019-01-14T12:22:39.0027228Z generate Report ReportTemplate.cshtml
2019-01-14T12:22:40.2872092Z reports generated
2019-01-14T12:22:40.2876384Z Result: tests executed with warnings
2019-01-14T12:22:40.2876686Z Total: 0
2019-01-14T12:22:40.2876843Z Succeeded: 0
2019-01-14T12:22:40.2876982Z Ignored: 0
2019-01-14T12:22:40.2877128Z Pending: 0
2019-01-14T12:22:40.2877260Z Skipped: 0
2019-01-14T12:22:40.2877390Z Failed: 0
2019-01-14T12:22:40.2877504Z
我的项目中包含以下软件包:
我的构建管道看起来像这样,我没有对任何任务做任何事情,我的猜测是我缺少测试程序集上的一些设置,但是我不确定它可能是什么。据我所读,只要正确的包在它试图构建的程序集中,它就应该能够默认测试:
我真的希望有人能在正确的方向指点我,谢谢。
更新基于Andreas Willich的回答我已经做了以下事情
我将测试分为两个任务,一个用于单元测试,另一个用于验收测试。 单元测试在默认的MSTest.TestAdapter上运行,并像以前一样成功完成。
我正在尝试在TechTalk.SpecRun.VisualStudio.TestAdapter
上运行验收测试我没有成功完成这项工作。它给了我以下输出:https://pastebin.com/mAR9HK2r
我不确定要在测试程序集和csproj文件中查找什么,因此我没有设法完成这些步骤。
答案 0 :(得分:1)
首先,我将使用两个单独的任务进行单元测试和验收测试。这样就可以为它们使用不同的测试适配器(例如NUnit和SpecFlow + Runner)。
如果使用SpecFlow进行验收测试,则需要SpecFlow软件包。您只需要选择使用哪个测试适配器即可。根据您要使用的测试适配器,查看此答案所需的软件包:https://stackoverflow.com/a/38990326/3155323
另外,我将程序集筛选器限制为您测试程序集。这样可以减少大量注销,因为测试适配器必须扫描较少的程序集。
切换到NUnit时,请确保已在app.config中将NUnit配置为unitTestProvider并重新生成了所有代码隐藏文件。
下一步,我将打开测试程序集,并查看生成的测试是否可用。如果没有,请查看您的csproj文件,如果代码隐藏文件已添加到CompileItemGroup。
很抱歉,这不是“您已忘记选中此框”的答案。当测试适配器找不到任何测试时,有多种可能的小原因。
答案 1 :(得分:0)
我将发布为使SpecFlow测试结果显示在Azure DevOps Builds-> Tests区域中所做的事情,但是请注意,这是一个 giant 黑客。
我相信与常规NUnit测试一样,应该支持SpecFlow / NUnit测试,而且我确实像Andreas Willich所说的那样围绕测试适配器配置,但是我都无法获得它对我有用,我也找不到任何人可以通过正常管道配置使它起作用的示例。 SpecFlow +也可能有工作方式,但我不使用plus版本。如果/当我可以学习正确的方法时,我将停止使用以下内容。
创建一个简单的过程,以将SpecFlow测试名称和测试结果值写入CSV格式的光盘中。之所以这样做,是因为我想将功能测试解决方案与该hack发生的任何其他逻辑分开。此过程应该很简单,就像使用[AfterScenario]钩子步骤从Scenario.Context中提取Scenario标题和结果值(或在任何可以获取的位置)并将它们以逗号分隔的值格式写入文本文件一样。
将该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
该类应如下所示:
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区域中显示这些结果。
在一个新的单独的“转换”项目中,创建一个流程,该流程将从您的“ SpecFlow”项目中读取简单的CSV输出结果文件,并在“虚拟”项目,其中包含NUnit测试。
“虚拟”项目中的单个NUnit测试方法现在将被替换为多种方法,对于记录在CSV中的每个结果都将使用一种方法。这些方法将被命名为guids减去任何破折号,并以字母开头,使其成为有效的C#方法名称。目标只是拥有非重复的方法名称。实际的SpecFlow测试方案名称将存储在NUnit TestCase TestName属性中。对于从CSV文件读取的关联结果值,将使用Assert.Pass()或Assert.Fail()。将此“转换”项目编译为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(); }
}}
现在执行以下步骤:
Azure管道现在将使用存储在CSV中的名称和结果来构建“ SpecFlow”项目的测试执行结果并将其显示为“ SpecFlow测试”(我也有一些演示ID)
可以通过不同的方式进行设置;一种方法是:
此管道现在将在推送到其存储库的提交时自动触发,该过程将自动将SpecFlow结果转换为NUnit结果,触发辅助管道,并在第二秒的Builds-> Tests区域中显示SpecFlow结果ple第一个管道不会显示任何结果,您总是会在第二个管道中看到它们。
要以这种方式支持更多/其他“ SpecFlow”项目...它们可能都共享“转换”部分,但是需要进行改进以使其能够传递到其中。正在更新的cs文件。然后,您可以以这种方式为要查看其结果的每个“ SpecFlow”项目创建单独的“虚拟”项目(以及相关的存储库/管道)。到目前为止,我还没有做完这件事,因为我只为一个项目做这件事。