使用Specflow在Azure开发人员上运行自动验收测试

时间:2019-01-14 14:33:30

标签: azure-devops specflow

我正在尝试建立一个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

我的项目中包含以下软件包:

  • Nunit软件包
  • SpecFlow程序包
  • NUnit3TestAdapater
  • SpecRun.Runner
  • SpecRun.SpecFlow

我的构建管道看起来像这样,我没有对任何任务做任何事情,我的猜测是我缺少测试程序集上的一些设置,但是我不确定它可能是什么。据我所读,只要正确的包在它试图构建的程序集中,它就应该能够默认测试: enter image description here

我真的希望有人能在正确的方向指点我,谢谢。

更新基于Andreas Willich的回答我已经做了以下事情

将这些软件包实施到验收测试项目中 enter image description here

我将测试分为两个任务,一个用于单元测试,另一个用于验收测试。 enter image description here 单元测试在默认的MSTest.TestAdapter上运行,并像以前一样成功完成。 enter image description here

我正在尝试在TechTalk.SpecRun.VisualStudio.TestAdapter enter image description here

上运行验收测试

我没有成功完成这项工作。它给了我以下输出:https://pastebin.com/mAR9HK2r

我不确定要在测试程序集和csproj文件中查找什么,因此我没有设法完成这些步骤。

2 个答案:

答案 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版本。如果/当我可以学习正确的方法时,我将停止使用以下内容。

  1. 创建一个简单的过程,以将SpecFlow测试名称和测试结果值写入CSV格式的光盘中。之所以这样做,是因为我想将功能测试解决方案与该hack发生的任何其他逻辑分开。此过程应该很简单,就像使用[AfterScenario]钩子步骤从Scenario.Context中提取Scenario标题和结果值(或在任何可以获取的位置)并将它们以逗号分隔的值格式写入文本文件一样。

  2. 将该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
  1. 创建一个单独的简单“虚拟”项目,其中仅包含一个类,该类具有一个有效的NUnit“测试”方法,该方法仅对Assert.Pass()起作用。该项目将需要安装NUnit和NUnit3TestAdapter NuGet软件包。

该类应如下所示:

    namespace DemoNunit
    {
        public class Tests
        {
            [Test]
            public static void DemoTest005()
            {
                Assert.Pass();
            }
        }
    }
  1. 为此“虚拟”项目的解决方案创建一个Azure DevOps Git代码存储库,并将其推送到存储库中。

  2. 创建为CI构建配置的新Azure管道,当将对“虚拟” NUnit项目的提交推送到其存储库时,它将自动触发。配置管道以使其具有一个名为“ SpecFlow Tests”的Visual Studio测试步骤,该步骤将查找包含该单个NUnit测试的dll。为您的“ SpecFlow”项目或其测试的功能命名该管道,因为该管道实际上最终将在Builds-> Tests区域中显示这些结果。

  3. 在一个新的单独的“转换”项目中,创建一个流程,该流程将从您的“ 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();        }
    }}

现在执行以下步骤:

  • 使用命令行通过命令行执行“ SpecFlow”项目的测试 NUnit控制台TestRunner(应配置为删除CSV 输出到c:\ Temp)这样的地方
  • 执行您的“转换”项目,该项目应配置为读取 在c:\ Temp中使用CSV格式,并在“虚拟”项目中更新.cs文件 光盘上克隆克隆存储库中的任何地方
  • 通过命令行参数,进行git commit并推送到仓库 将“转换” exe文件更改为“虚拟”项目的.cs文件

Azure管道现在将使用存储在CSV中的名称和结果来构建“ SpecFlow”项目的测试执行结果并将其显示为“ SpecFlow测试”(我也有一些演示ID) enter image description here

可以通过不同的方式进行设置;一种方法是:

  • 将您的“ SpecFlow”项目放入git repo并创建CI管道 为此
  • 添加构建步骤集以通过命令行执行测试
  • 添加一个步骤来调用已放置在 固定在构建框中的位置,并更新“虚拟”项目的文件,该文件位于共享位置的单独VM中
  • 添加一个步骤,将最新的更新推送到git repo的“虚拟”项目中。

此管道现在将在推送到其存储库的提交时自动触发,该过程将自动将SpecFlow结果转换为NUnit结果,触发辅助管道,并在第二秒的Builds-> Tests区域中显示SpecFlow结果ple第一个管道不会显示任何结果,您总是会在第二个管道中看到它们。

要以这种方式支持更多/其他“ SpecFlow”项目...它们可能都共享“转换”部分,但是需要进行改进以使其能够传递到其中。正在更新的cs文件。然后,您可以以这种方式为要查看其结果的每个“ SpecFlow”项目创建单独的“虚拟”项目(以及相关的存储库/管道)。到目前为止,我还没有做完这件事,因为我只为一个项目做这件事。