Writing Acceptance Tests using SpecFlow & Selenium
Selenium and SpecFlow are pretty straight forward to get to grips with individually, but on larger projects when the page object pattern is used things start to get a little more complicated. I find it useful to think about three layers when organizing the code that runs the tests...
The important thing to take note of in this code excerpt, is that the page objects are stored in the Specflow ScenarioContext. You can use instance variables, but its better to use the context for when you start sharing steps across multiple feature files.
Also note the pattern for navigating to a new page. The home page object has a GoToRegisterPage which returns a RegisterPage object which is then available for the next step to use.
Source: Selenium HQ Docs
Now we are ready to start using the web driver to find elements on the page. Of course the code to do that is actually in our page object classes, so here is sample code of what a HomePage and RegisterPage class:
The By class provides the functionality to find by id, name, link text, class name, css and more so it really is very flexible. By the time you have the feature files, steps and page objects all set up, accessing the page with Selenium is comparatively straight forward.
- SpecFlow Scenarios are written in a feature file using Cucumber syntax
- Each part of the scenario is implemented in a steps file.
- The steps call into page objects which act as an API using Selenium Webdriver to access the page.
Setting up SpecFlow Feature Files
- Install the SpecFlow Extension for Visual Studio which provides the feature file and hook file templates, as well as a context menu options for running/debugging tests.
- Create a Visual Studio Class Library project for the tests.
- Install the SpecFlow nuget package for the dll (SpecFlow.NUnit)
- Add a feature file and add in some "given, then, when" statements:
- Next add a class file for the feature steps and use the extension tooling to generate the steps by right clicking on the scenario and selecting Generate Step Definitions and copying in the code.
- Add the Binding attribute to the top of the steps class and you should now have a test that runs, all be it with some pending method calls.
Writing the Steps Class
The Steps class uses page objects to actually interact with the page so we don't need to know about Selenium at this level. Each page object exposes methods that represent actions on that page, like EnterDetails or ClickRegister below.The important thing to take note of in this code excerpt, is that the page objects are stored in the Specflow ScenarioContext. You can use instance variables, but its better to use the context for when you start sharing steps across multiple feature files.
Also note the pattern for navigating to a new page. The home page object has a GoToRegisterPage which returns a RegisterPage object which is then available for the next step to use.
Source: Selenium HQ Docs
Page Objects and Selenium WebDriver
Setting up the Selenium WebDriver is as simple as installing the appropriate Selenium Nuget package (Selenium.WebDriver) The Selenium API documentation is good, so its easy enough to get started finding elements and clicking buttons etc. First up you need to initialize the web driver object:
Now we are ready to start using the web driver to find elements on the page. Of course the code to do that is actually in our page object classes, so here is sample code of what a HomePage and RegisterPage class:
The By class provides the functionality to find by id, name, link text, class name, css and more so it really is very flexible. By the time you have the feature files, steps and page objects all set up, accessing the page with Selenium is comparatively straight forward.
Test Set Up using the Hooks
As you may have noticed above, the web driver, once initialized is stored in the FeatureContext. This code fragment sits in the BeforeFeature hook. There are before and after hooks at a feature level and a scenario level. So the last piece of the puzzle is to be aware that the web driver object is intialized and stored in the FeatureContext using the BeforeFeature hook AND the HomePage object is initialized at the beginning of every scenario and stored in the ScenarioContext. This is also the place that you would do any database setup and/or teardown.
I have a sample acceptance test solution setup on GitHub if you want to see the theory above in action. It uses the ASP.NET MVC 4 internet template to provide some simple test scenarios.