I understand that within this framework users are unable to call scenarios within scenarios.
I am trying to create End to End test cases where there are validation points at multiple stages of the test.
Using 'bad' gherkin syntax the process for the scenario would be something like:
Given Item A exists
When User processes Item
Then Warning is displayed
When User accesses Item
Then Warning is not displayed
When User finalises Item
Then Item status = "CURRENT"
And Record Status = "COMPLETED"
The first thing I considered was breaking the scenario into 3 distinct GWT scenarios.
That is fine.
However suppose I now want to create a new end to end scenario that can re-use one of the 3 scenarios created (as you would re-use a function).
How do I do this without duplication of Gherkin code?
I cannot use Background as the sections that I require to re-use are in the middle of the execution.
Steps prior and after may be different.
IN SUMMARY: I am trying to re-use a GWT scenario that is common for many end to end scenarios where the end to end scenarios are inherently different.
Any feedback or assistance you can provide would be greatly appreciated.
Cheers and thanks,
I think I know what you mean. I have done some "meta" scenario's. For example there are ones that test login, maintaining details and creating an account that are very fine grained. Then I have scenario's that test things further along the way, assuming that the happy path has been taken up to that point. So imagine having a scebnario called:
The user is logged in and has successfully created an account and is at the add product screen
For that, in the step definition, I would just combine function calls with hardcoded values:
#When("^The user is logged in and has successfully created an account and is at the add product screen$")
def addProducts(String val) {
navLibrary.loginAsUser("user", "password")
navLibrary.createAccount("some", "params", "you", "need")
navLibrary.navToProducts("some param")
}
And then start with the finer details in new separate steps. You have to think system design, and cascading. For me it meant a lot of initial rework as the tests started growing, but now it's a breeze. Pick the right level of reusability. It is testing code, so it doesn't have to subscribe perfectly to all the programming precepts. It must work, and it must be low maintenance in the long run.
I didn't use Cypress though. My tests are in Groovy with Selenium and Cucumber.
Background
I'm writing both a cucumber library with a variety of different steps to use across multiple projects and attempting to reduce complexity of step definitions by splitting them into 3 different modules, for iOS, Android and Web - specifically projects that include all 3.
One of the steps libraries will include a load of security-based steps that I want to explicitly include into a project before using the steps.
The project split library will be explicitly included depending on what has been specified in the configuration:
if $profile[:app_type] == 'android'
World(ProjectSteps::Android)
else
if $profile[:app_type] == 'ios'
World(ProjectSteps::IOS)
else
World(ProjectSteps::Web)
end
end
These are not to replace the helper methods, but to save us time when starting up on new projects and will also allow us to have project specific step definitions written in different ways depending on whether we are testing the web, native iOS app or native Android app built to have the exact same functionality, but are different enough to require a different step definition
The Issue
After defining the step within a module, the feature files can still execute this happily, even if the module has not been included in the "World" like this: World(CommonSteps::Security), which is what you would usually use to let cucumber know about helper methods hidden away inside of modules.
When 'I provide my personal details' do
select :title, 'Mr'
fill :first_name, 'John'
fill :last_name, 'Doe'
unless $profile[:app_type] == 'web'
click :progress
end
if $profile[:app_type] == 'android'
fill :postcode, 'TE37ER'
select :address, '1 Test Street'
click :progress
fill :occupation, 'Tester'
fill :company, 'Test Company'
click :progress
else
fill :occupation, 'Tester'
fill :company, 'Test Company'
unless $profile[:app_type] == 'web'
click :progress
end
fill :postcode, 'TE37ER'
select :address, '1 Test Street'
click :progress
end
end
This step definition is trying to test 3 apps at once, but it is testing the exact same functionality, the exact same scenarios and the exact same features. If this was split into 3 step definitions, then it would be simpler to debug in the future, but the same feature file would be able to be used for each of them (which isn't out of the question, as there are many apps that share the exact same functionality across web and native mobile variants). In my opinion, this type of step definition is trying to achieve too much.
This would be easier to maintain despite being more, as it is simpler:
module ProjectSteps::IOS
When 'I provide my personal details' do
select :title, $user[:title]
fill :first_name, $user[:first_name]
fill :last_name, $user[:last_name]
click :progress
fill :occupation, $user[:occupation]
fill :company, $user[:company]
click :progress
fill :postcode, $user[:postcode]
select :address, $user[:line1]
click :progress
end
end
module ProjectSteps::Android
When 'I provide my personal details' do
select :title, $user[:title]
fill :first_name, $user[:first_name]
fill :last_name, $user[:last_name]
click :progress
fill :postcode, $user[:postcode]
select :address, $user[:line1]
click :progress
fill :occupation, $user[:occupation]
fill :company, $user[:company]
click :progress
end
end
module ProjectSteps::Web
When 'I provide my personal details' do
select :title, $user[:title]
fill :first_name, $user[:first_name]
fill :last_name, $user[:last_name]
fill :occupation, $user[:occupation]
fill :company, $user[:company]
fill :postcode, $user[:postcode]
select :address, $user[:line1]
click :progress
end
end
When 'some thing that is the same across platforms' do
# Some stuff
end
Bear in mind, this is a simple version of what I'm trying to achieve, and doesn't show the full complexity of some of the issues that I'm trying to resolve. In this case, I would most likely go with the if/unless version rather than the split, but there are a few cases that are drastically more complex, and would benefit from being split into 3 sections.
We could also add silent checks for bugs that we found in development to make sure that these don't regress, and as the web, android and ios apps feature different bugs, we'd end up with a large amount of if/unless statements.
What have I tried? - I hear you ask
Well I'm either really close, or really far off.
Given, When and Then don't work as expected when within a different module, which is why I had to search for the method that I believe them to be an alias of.
Here is the resulting code:
require_relative 'xss.rb'
require 'cucumber'
module CommonSteps
module Security
Cucumber::RbSupport::RbDsl.register_rb_step_definition(
'I attempt to write a step definition that has to be included to work',
Proc.new {
# Some stuff here
})
end
end
Registers the step definition absolutely fine. But that's part of the problem. I only want to register this step definition if the module has been included in the world of the project that I'm working on.
It would also mean that we can switch out the Web steps for the iOS and Android steps if we need to, while keeping the feature files the exact same. (Yes I know if statements are a thing, but too many, and step defs get complicated really fast.)
Edit
What I'm trying to achieve is not like the "Web steps" that we have seen in the past. No generic steps show code and are only in the language that we have agreed with the businesses that we work with, alongside the development team. As a lot of the projects we work on are cross platform, I'm in essence trying to achieve something that will switch which type of step definition is being used. - if you're using Chrome, use the Web version of this step definition, if you're using iOS, use the iOS version of this step definition, but also a means to include a variety of generic steps that are powered by text, which can link back to our page object model - keeping the scenarios completely business-based.
Given I am on the "Personal Details" page # (generic)
When I provide my personal details # (non-generic, but Web, iOS and Android versions exist)
But leave the "First Name" field blank # (generic)
And I attempt to continue to the next page # (generic)
Then I should see a validation error for the "First Name" text box stating: "Please provide your first name" # (generic)
For example, and if the validation is something that the business wants to know is working, and it'a part of the requirements that have been agreed with the business, is there a more business understandable way to communicate that information? - the why in this, is that we want to make sure the user fills in the information so that the validation does not show, but in the case that they don't provide that information, we should be also testing for the validation appearing in scenarios where it is needed.
We use the page object model in such a way that "Personal Details" will find the :personal_details key in the urls map. The key can be passed down to the next steps, linking to the Pages.personal_details method, which contains the :first_name key. All of our projects use this setup, and it's part of the documentation that goes with our core helper method library.
What I'm trying to achieve is not necessarily bad practice when used in the way that I'm suggesting, but could, if used incorrectly, be used as such.
There have been quite a few times in Cucumber's history when things like this have been done Cucumber itself used to have web steps, which was removed some time ago. These are now in a Gem https://github.com/cucumber/cucumber-rails-training-wheels. You might get some tips from that for your library.
That said, I would strongly recommend not writing a library of step definitions. Instead I would write a library of methods that can be used by step definitions. A crude example might help illustrate this.
Lets say you have a really complex method for logging in to an application. You could write a really commplicated step definition that deals with all sorts of logging in things such as
When I login (hugely complex regex to deal with things like ...
# loads of code to deal with you params and regex's and make things work with lots of different scenarios
or you could write a method like
def login(as:, args={})
and let people use this method when they write stuff e.g.
When 'I login' do
login as: #i
end
When 'I login as Fred' do
login as: create_or_find_user(firstname: 'Fred')
end
or
When 'I login as Fred with Jill's password' do
login as: #fred, password: #jill.password
end
The helper methods provide utility to help you write simple step definitions that are appropriate to your individual context. A shared step definition restricts you to using something that is highly complex and cannot have anything context specific.
Scenarios should be context specific and allow flexible simple language that is specific to the context of the individual World they are part of. They should be all about Why something is being done and What that thing is, and have nothing about How something is being done. By definition they do not share and so by definition neither to step definitions.
Once you've left a step definition by making a call you are into the realm of code, and code is really effective at sharing
Cucumber has learnt the lesson that shared step definitions are a really bad idea (see http://aslakhellesoy.com/post/11055981222/the-training-wheels-came-off). Be wary of repeating past mistakes.
Context:
I'm trying to make reusable step definitions that click on page objects on the current page,
e.g. (cucumber step def follows):
When(/^the user clicks the "([^"]*)" button$/) do |button|
click_button = button.downcase.gsub(" ","_")
#current_page #somehow get current page object on this line
#current_page.click_button
end
Problem statement:
I can't find anything that returns the current page object.
An explanation for why the obvious solution didn't work:
I thought #current_page was already there as something I could use. I looked in the source code for page object, and the variable #current_page does exist. Not sure how to use it if I can...
BTW, in this case, I have a bunch of testers that can write Gherkin but not necessarily step definitions. We are trying to rapidly finish a bunch of regression tests for an in house app with an unchanging interface.
This is somewhat at odds with what page-object is trying to provide.
Page object attempts to provide well named actions for interacting with a specific page. If you are wanting to make something that works in general against any page, it will be much easier to write it with watir-webdriver directly.
That said, I agree that a specification based heavily on implementation like that is likely to change. I also would add that it doesn't add much value. I would only continue down this path if you understand and accept that you are using cucumber as a test templating tool instead of a requirements communication tool.
As Justin Ko mentioned, #current_page gets set when you call the on or visit methods. Its not a good idea to lump something that changes the page object in a step that performs a specific action (in this case clicking a button). You might want a different step that indicates the behavior of the application, such as
the application lands on the <your page> page
Then you're can use the name of the page object class to load #current_page via the on method in that step definition. This also gives the benifit (or curse of having your step having more lower level details) of indicating expected page navigation behavior.
I have a set of functionally similar websites that I want to write cucumber specs for to drive both development, and selennium browser tests. The site are in different languages and will have different URLs, but will have mainly the same features.
An example scenario might be
Scenario Outline: Photo Gallery Next Action
Given I visit a "<photo-gallery-page>"
When I click "<next-button>" in the gallery
Then the photo should advance
Examples:
| photo-gallery-page | next-button |
| www.site1.com/photo-gallery | Next |
| www.site2.com/la-galerie-de-photos | Suivant |
This is fine when I have a small number of scenarios and examples. However I'm anticipating hundred of scenarios and fairly regular launch of new sites. I want to avoid having to edit each scenario to add examples when launching new sites.
I think I need to store all my example variables in a per site configuration, so that I can run the same scenario against all sites. Then I can add new configurations fairly easily and avoid editing all the scenario examples and making them unreadable.
site[:en].photo-gallery-page = 'www.site1.com/photo-gallery'
site[:fr].photo-gallery-page = 'www.site2.com/la-galerie-de-photos'
site[:en].next-button = 'Next'
site[:fr].next-button = 'Suivant'
One option would be to store this config somewhere, then generate the site specific gherkin files using a script. I could then run these generated gherkins which would contain the required examples
I'm wondering if there's an easier way. My other idea was if I can use table transforms to replace the example blocks. I've had a read, but as far as I can tell I can only transform a table (and replace it with a custom code block) if it's an inline table within a step. I can't transform an examples block in the same way.
Have I understood that correctly? Any other suggestions on how best to achieve this?
I wonder if there's a better way... This all feels very brittle.
What if:
Given I follow a link to the gallery "MyGallery"
And the gallery "MyGallery" contains the following photos:
|PhotoID|PhotoName|
|1 |MyPhoto1 |
|2 |MyPhoto2 |
And the photo "MyPhoto1" is displayed
When I view the next photo
Then the next photo "MyPhoto2" should be displayed
Note that you've taken out the notion of button names, etc. - implementation details that are presumably better defined in your step definitions. The behaviour you're defining is simply going to a gallery, viewing an image, requesting the next one, viewing the next image. Define how in your step definitions.
There's some reading I found very useful on this topic at http://cuke4ninja.com/. Download the PDF and check out the web automation section (it details the web automation pyramid).
To address your configuration problem, maybe you could define some kind of config. class and supply it to the step definition files via dependency injection. You could make it site specific by loading from different config. files as you suggested in its constructor. Step definitions could pull the relevant site specific data from the config. class' properties. I think this would make your scenario is more readable and less brittle.
For my RSpec tests I would to automatically associate data files with each test. To clarify, if my tests each require an xml file as input data and then some xpath statements to validate the responses they get back I would like to externalize the xml and xpath as files and have the testing framework easily associate them with the particular test being run by using the unique ID of the test as the file(s) name. I tried to get this behavior but the solution isn't very clean. I wrote a helper method that takes the value of "description" and combines it with FILE to create a unique identifier which is set into a global variable that other utilities can access. The unique identifier is used to associate the data files I need. I have to call this helper method as the first line of every test, which is ugly.
If I have an RSpec example that looks like this:
describe "Basic functions of this server I'm testing" do
it "should give me back a response" do
# Sets a global var to: "my_tests_spec.rb_should_give_me_back_a_response"
TestHelper::who_am_i __FILE__, description
...
end
end
Is there some better/cleaner/slicker way I can get an unique ID for each test that I could use to associate data files with? Perhaps something build into RSpec I'm unaware of?
Thank you,
-Bill
I just learned about the nifty global before and after hooks. I can hide the unique ID creation code there. It makes things much cleaner. I'll probably go with this solution unless there's an even slicker way to acquire a unique ID for each test. Thanks