Typically when I create a Specflow BDD solution, BDD elements are separated into Feature Files, Hooks, Step Definitions and Pages. These elements are separate folders or subsets of folders within the solution.
Code begins with the feature files written in Gherkin, feature files contain Scenarios which themselves execute test cases. Sometimes a scenario is just one test case or can be many iterations of a particular action. An example is listed below:
Generally we perform a set of actions and then assert the outcome at the end, in the scenario above we are searching for links in the sitemap section of the footer, selecting a result from a list and then asserting that we are taken to that particular page.
It helps greatly if these features are written in collaboration. In an ideal world features should be authored in a “three amigos” style with the project Business Analyst and Product Owner.
Gherkin generates Step Definitions which can be used across a set of feature files. It makes sense to group common ones together in one big Step Definition file for actions such as navigating to the site or logging in/out of the application. More feature specific steps are kept in separate files.
It is our aim to keep the code in step definitions short and clean, we do this by wrapping up selenium code and putting these methods in to the Pages .cs files. For example:
Given I have navigated to Test Environment
Generates the following Step Definition:
public void GivenIHaveNavigatedTo(string website)
From here in one of the common Pages .cs files we have created the following method:
We try to keep the Selenium code wrapped up in these statements to keep the Step definitions easy to write. With this method in place our step definition looks like this:
public void GivenIHaveNavigatedTo(string website)
Tests are organised with @Tags which make groups of or individual tests easy to find in MS Test. They also have other important uses in hooks and can be used to execute a set of tests on a CI build.
For example all Sanity tests are labelled with @_Sanity this makes the whole suite of sanity tests easy to find and execute. If this tag was passed to a CI build then rather handily just the sanity tests would run on an overnight build rather than a lengthier @_Regression suite.
What’s with the underscore? Well that just keeps your tags at the top of the MS Test list if you happen to be using that test framework, depending on your preference of viewing them.
Hooks are used primarily for the test run set up and teardown. Before a run we want to instantiate a driver and spin a browser up before any scenarios are automated, otherwise it would go nowhere fast. Similarly at the end of a test run we want to kill the browser off.
You can be a bit more clever with hooks and execute bits of code before or after certain features, steps, scenarios or scenario blocks. In order to keep an easy to understand codebase these must be used sparingly. This is because debugging can be harder and feature files can have all sorts of things happening that aren’t specified in English and that’s not what BDD is intended for.
For more information on hooks please visit: https:/
This section of classes is where we keep all of the wrapped up Selenium code, which is a bit nasty. We wrap them up into helper methods to make our step definitions easier to write as described previously. This code should be kept clean and refactored and should encapsulate common practices and apply sensible programming practices such as DRY (Don’t Repeat Yourself).
The Selectors Page is where we keep a large number of constants, this is so that if an Id changes on the System Under Test you will only ever need to change it once to fix your broken test(s).
We also keep in mind a set of rules when structuring the code which are:
- No Selenium Code in Step Definitions
- No Project Specific Methods in PageBase, use MainPage for these
- All By.Criteria to be defined and referenced in selectors page
- URLs and base URLs that aren’t in Gherkin must be constants
- Create triptych Given/When/Then step definitions where necessary
- Always copy generated methods to clipboard
- No Thread.Sleeps in any step definitions, wait for something instead
- Regions, regions, regions – use regions in code to keep it nice and tidy
- “That” is a banned word in Gherkin
- Replace all auto generated variable names with something sensible
- All assertions have the optional message parameter populated except for AreEqual
- Split Step Definitions out into sensible groupings/ filenames
You can find a base solution here.
There is a lot more you can do to improve this solution like putting in a clever environment selector, addition of Sauce Labs or BrowserStack and also Applitools Eyes. The way this is build is designed to give you scalability and organisation to cope as your test suite grows but you have to be strict with yourself and keep in mind the BDD Rules set out or things can get messy very quickly.