0
votes

I'm pretty new to Specflow and C#, so I'm facing an issue with specflow hooks. The problem is: when I use [BeforeScenario], the method is not even called while debugging. Removing these hooks and replacing it by [TestInitialize], it works perfectly. I searched here for solution in many questions, but I didn't find any problem besides something about private methods, which not seems to be my case.

I have 4 classes: Tests, Steps, PageObjects, and Hooks (which contains driver and hooks). 'Tests' class inherits from 'Steps', which inherits from 'PageObjects', which inherits from 'Hooks'. Every call is public and I'm writing down some code from 'Hooks' class:

 namespace AutomationPractice.Helper
{
    [Binding]
    public class Hooks
    {
        public IWebDriver _driver;

        [BeforeFeature]
        public void BeforeScenario()
        {
            if (_driver == null)
            {
                _driver = new ChromeDriver();
            }
            else { throw new Exception("Couldn't initialize the driver"); }

        }

        [AfterFeature]
        public void AfterScenario()
        {
            if (_driver != null)
            {
                _driver.Quit();
            }
            else throw new Exception("There was an error while trying to close the driver");

            }
}
}

'PageObjects' class:

namespace AutomationPractice.PageObjects
{
    [Binding]
    public class GoogleSearchPageObjects : Hooks
    {
        public string goToGooglePage(string url)
        {
            return _driver.Url = url;
        }

        public IWebElement GetTxtSearch()
        {
            return _driver.FindElement(By.Name("q"));
        }

        public void fillTxtSearch(string search)
        {
            GetTxtSearch().SendKeys(search);
        }

    }
}

'Steps' class:

namespace AutomationPractice.Steps
{
    [Binding]

    public class GoogleSearchSteps : GoogleSearchPageObjects
    {

        [Given(@"I am on google home page")]
        public void GivenIAmOnGoogleHomePage(string url)
        {
            goToGooglePage(url);

        }

        [When(@"I fill the '(.*)' field")]
        public void WhenIFillTheField(string search)
        {
            fillTxtSearch(search);
        } 

Every class is rounded by [Binding] though.

Thanks in advance!

2
Hi Fabio, in my code the Hooks class inherits the TechTalk.SpecFlow.Steps class. Can you try if this works for you? So public class Hooks : TechTalk.Specflow.Steps {} - Jeroen Lamberts
Hi @JeroenLamberts,, unfortunately it does'nt work. Generates a "circular base class dependency between 'GoogleSearchSteps' and 'Hooks'. - Fabio Cardoso
Try removing from [Binding] from Hooks and GoogleSearchPageObjects and run again - user1207289
@user1207289, It doesn't work. - Fabio Cardoso

2 Answers

0
votes

Your methods are names BeforeScenario and AfterScenario, but you are using the attributes for BeforeFeature and AfterFeature. These have to be static that they will be called.

You need to change the attributes.

0
votes

You have too many things going on in the same class hierarchy. It would be much simpler to decouple the following things:

  • The Web Driver
  • The page objects
  • Step definitions

You can use SpecFlow's dependency injection framework to wire these things together using constructor arguments.

First your Hooks class where you manage the web driver instance for all step definitions and page objects:

[Binding]
public class Hooks
{
    private IObjectContainer container;

    public Hooks(IObjectContainer container)
    {
        this.container = container;
    }

    [BeforeScenario]
    public void CreateWebDriver()
    {
        var driver = new ChromeDriver();

        container.RegisterInstanceAs<IWebDriver>(driver);
    }

    [AfterScenario]
    public void DestroyWebDriver()
    {
        var driver = container.Resolve<IWebDriver>();

        driver.Quit();
        driver.Dispose();
    }
}

And the google search page object becomes a separate class that receives a web driver object as a constructor parameter, which decouples it from SpecFlow all together.

public class GoogleSearchPage
{
    private readonly IWebDriver driver;

    private IWebElement TxtSearch => driver.FindElement(By.Name("q"));

    public GoogleSearchPage(IWebDriver driver)
    {
        this.driver = driver;
    }

    public void EnterSearchTerm(string searchTerm)
    {
        TxtSearch.SendKeys(searchTerm);
    }
}

And finally the step definition class, which is where everything gets wired together via the dependency injection framework that comes with SpecFlow:

[Binding]
public class GoogleSearchSteps
{
    private GoogleSearchPage googleSearch;

    public GoogleSearchSteps(IWebDriver driver)
    {
        googleSearch = new GoogleSearchPage(driver);
    }

    [When(@"I fill the '(.*)' field")]
    public void WhenIFillTheField(string search)
    {
        googleSearch.EnterSearchTerm(search);
    }
}

Part of the problem you have right now is the class hierarchy. You are mixing classes that should be separated, but coordinated. By separating the step definitions from the initialization of the web driver, and keeping the page object in its own class you keep the dependencies between these objects organized and limited to exactly what they need (decoupling), and yet still allow them to work together (cohesion).