SpecFlow: Uruchamianie testów za pomocą SpecRun

W poprzednich postach, używałem domyślnego runner’a dla danego framework’u.  Standardowo SpecFlow może być zintegrowany np.  z nUnit lub Mbunit. Jeśli tylko to możliwe, polecam zainstalowanie SpecRun, bo jak zaraz pokażę, jest dużo wygodniejszy w przypadku BDD.

Na początku sprawdźmy, co jest wygenerowane dla poniższego testu:

Feature: NewPost Jakis opis tutaj... Scenario: Create a new post Given I have logged into CMS When I press the create a post button Then article should be created.

Code-behind:

// ------------------------------------------------------------------------------ // <auto-generated> // This code was generated by SpecFlow (http://www.specflow.org/). // SpecFlow Version:1.9.0.77 // SpecFlow Generator Version:1.9.0.0 // Runtime Version:4.0.30319.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> // ------------------------------------------------------------------------------ #region Designer generated code #pragma warning disable namespace ConsoleApplication4 { using TechTalk.SpecFlow; [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.77")] [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [NUnit.Framework.TestFixtureAttribute()] [NUnit.Framework.DescriptionAttribute("NewPost")] public partial class NewPostFeature { private static TechTalk.SpecFlow.ITestRunner testRunner; #line 1 "NewPost.feature" #line hidden [NUnit.Framework.TestFixtureSetUpAttribute()] public virtual void FeatureSetup() { testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "NewPost", " Jakis opis tutaj...", ProgrammingLanguage.CSharp, ((string[])(null))); testRunner.OnFeatureStart(featureInfo); } [NUnit.Framework.TestFixtureTearDownAttribute()] public virtual void FeatureTearDown() { testRunner.OnFeatureEnd(); testRunner = null; } [NUnit.Framework.SetUpAttribute()] public virtual void TestInitialize() { } [NUnit.Framework.TearDownAttribute()] public virtual void ScenarioTearDown() { testRunner.OnScenarioEnd(); } public virtual void ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) { testRunner.OnScenarioStart(scenarioInfo); } public virtual void ScenarioCleanup() { testRunner.CollectScenarioErrors(); } [NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Create a new post")] public virtual void CreateANewPost() { TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Create a new post", ((string[])(null))); #line 3 this.ScenarioSetup(scenarioInfo); #line 4 testRunner.Given("I have logged into CMS", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); #line 5 testRunner.When("I press the create a post button", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); #line 6 testRunner.Then("article should be created.", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); #line hidden this.ScenarioCleanup(); } } } #pragma warning restore #endregion

Z ciekawszych rzeczy, widzimy wyraźnie jak odpalany jest test:

[NUnit.Framework.TestAttribute()] [NUnit.Framework.DescriptionAttribute("Create a new post")] public virtual void CreateANewPost() { TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Create a new post", ((string[])(null))); #line 3 this.ScenarioSetup(scenarioInfo); #line 4 testRunner.Given("I have logged into CMS", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); #line 5 testRunner.When("I press the create a post button", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); #line 6 testRunner.Then("article should be created.", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); #line hidden this.ScenarioCleanup(); } }

CreateANewPost to zwykły test jednostkowy. Zaletą jest, że nie musimy nic zmieniać na serwerach CI. We wszelkich raportach jednak, będziemy mieli nazwę CreateANewPost, co nie jest zbyt eleganckie. Na przykład, w Test Explorer mamy:

image

Instalacja SpecRun odbywa się za pomocą NuGet:

image

 

W App.Config zobaczymy, że nUnit został zastąpiony SpecRun:

<specFlow> <!-- For additional details on SpecFlow configuration options see http://go.specflow.org/doc-config --> <!-- For additional details on SpecFlow configuration options see http://go.specflow.org/doc-config --><!-- use unit test provider SpecRun+NUnit or SpecRun+MsTest for being able to execute the tests with SpecRun and another provider --><unitTestProvider name="SpecRun" /><plugins> <add name="SpecRun" /> </plugins> </specFlow>

CodeBehind również zostanie ponownie wygenerowany:

// ------------------------------------------------------------------------------ // <auto-generated> // This code was generated by SpecFlow (http://www.specflow.org/). // SpecFlow Version:1.9.0.77 // SpecFlow Generator Version:1.9.0.0 // Runtime Version:4.0.30319.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> // ------------------------------------------------------------------------------ #region Designer generated code #pragma warning disable namespace ConsoleApplication4 { using TechTalk.SpecFlow; [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.9.0.77")] [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [TechTalk.SpecRun.FeatureAttribute("NewPost", Description=" Jakis opis tutaj...", SourceFile="NewPost.feature", SourceLine=0)] public partial class NewPostFeature { private static TechTalk.SpecFlow.ITestRunner testRunner; #line 1 "NewPost.feature" #line hidden [TechTalk.SpecRun.FeatureInitialize()] public virtual void FeatureSetup() { testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "NewPost", " Jakis opis tutaj...", ProgrammingLanguage.CSharp, ((string[])(null))); testRunner.OnFeatureStart(featureInfo); } [TechTalk.SpecRun.FeatureCleanup()] public virtual void FeatureTearDown() { testRunner.OnFeatureEnd(); testRunner = null; } public virtual void TestInitialize() { } [TechTalk.SpecRun.ScenarioCleanup()] public virtual void ScenarioTearDown() { testRunner.OnScenarioEnd(); } public virtual void ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) { testRunner.OnScenarioStart(scenarioInfo); } public virtual void ScenarioCleanup() { testRunner.CollectScenarioErrors(); } [TechTalk.SpecRun.ScenarioAttribute("Create a new post", SourceLine=2)] public virtual void CreateANewPost() { TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Create a new post", ((string[])(null))); #line 3 this.ScenarioSetup(scenarioInfo); #line 4 testRunner.Given("I have logged into CMS", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); #line 5 testRunner.When("I press the create a post button", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); #line 6 testRunner.Then("article should be created.", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); #line hidden this.ScenarioCleanup(); } [TechTalk.SpecRun.TestRunCleanup()] public virtual void TestRunCleanup() { TechTalk.SpecFlow.TestRunnerManager.GetTestRunner().OnTestRunEnd(); } } } #pragma warning restore #endregion

Nadal będziemy mieli metodę o nazwie CreateANewPost, ale atrybuty nUnit zostały zastąpione SpecRun:

[TechTalk.SpecRun.ScenarioAttribute("Create a new post", SourceLine=2)] public virtual void CreateANewPost() { TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Create a new post", ((string[])(null))); #line 3 this.ScenarioSetup(scenarioInfo); #line 4 testRunner.Given("I have logged into CMS", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given "); #line 5 testRunner.When("I press the create a post button", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); #line 6 testRunner.Then("article should be created.", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); #line hidden this.ScenarioCleanup(); } [TechTalk.SpecRun.TestRunCleanup()] public virtual void TestRunCleanup() { TechTalk.SpecFlow.TestRunnerManager.GetTestRunner().OnTestRunEnd(); } }

Warto upewnić się, że w opcjach (Tools->Options), został faktycznie ustawiony SpecRun:

image

W Test Explorer mamy teraz dużo czytelniejszą nazwę testu:

image

Co więcej, po odpaleniu testów, zostanie również wygenerowany raport:

image

Klikając na nazwę testu, dostaniemy ładne podsumowanie w formacie GWT+kod:

image

Przeglądając wygenerowane pliki w Solution Explorer, zobaczymy również runtests.cmd. Oczywiście to skrypt odpalający testy:

image

Jeśli ktoś posiada kilkadziesiąt testów, napisanych w SpecFlow, warto pomyśleć o SpecRun. Oprócz raportu i przejrzystości, zyskamy również możliwość równoległego wykonywania testów, co w przypadku testów systemowych, UI może mieć znaczenie. Niestety za darmo mamy do dyspozycji wyłącznie evaluation mode. Nie jest on limitowany czasowo, ale testy są specjalnie opóźniane co można zaobserwować w Test Explorer.,

Leave a Reply

Your email address will not be published.