SpecFlow - Is it possible to reuse test data within feature file? - cucumber

Is there any way to reuse data in SpecFlow feature files?
E.g. I have two scenarios, which both uses the same data table:
Scenario: Some scenario 1
Given I have a data table
| Field Name | Value |
| Name | "Tom" |
| Age | 16 |
When ...
Scenario: Some scenario 2
Given I have a data table
| Field Name | Value |
| Name | "Tom" |
| Age | 16 |
And I have another data table
| Field Name | Value |
| Brand | "Volvo" |
| City | "London" |
When ...
In these simple examples the tables are small and there not a big problem, however in my case, the tables have 20+ rows and will be used in at least 5 tests each.
I'd imagine something like this:
Having data table "Employee"
| Field Name | Value |
| Name | "Tom" |
| Age | 16 |
Scenario: Some scenario 1
Given I have a data table "Employee"
When ...
Scenario: Some scenario 2
Given I have a data table "Employee"
And I have another data table
| Field Name | Value |
| Brand | "Volvo" |
| City | "London" |
When ...
I couldn't find anything like this in SpecFlow documentation. The only suggestion for sharing data was to put it into *.cs files. However, I can't do that because the Feature Files will be used by non-technical people.

The Background is the place for common data like this until the data gets too large and your Background section ends up spanning several pages. It sounds like that might be the case for you.
You mention the tables having 20+ rows each and having several data tables like this. That would be a lot of Background for readers to wade through before the get to the Scenarios. Is there another way you could describe the data? When I had tables of data like this in the past I put the details into a fixtures class in the automation code and then described just the important aspects in the Feature file.
Assuming for the sake of an example that "Tom" is a potential car buyer and you're running some sort of car showroom then his data table might include:
| Field | Value |
| Name | Tom |
| Age | 16 |
| Address | .... |
| Phone Number | .... |
| Fav Colour | Red |
| Country | UK |
Your Scenario 2 might be "Under 18s shouldn't be able to buy a car" (in the UK at least). Given that scenario we don't care about Tom's address phone number, only his age. We could write that scenario as:
Scenario: Under 18s shouldnt be able to buy a car
Given there is a customer "Tom" who is under 16
When he tries to buy a car
Then I should politely refuse
Instead of keeping that table of Tom's details in the Feature file we just reference the significant parts. When the Given step runs the automation can lookup "Tom" from our fixtures. The step references his age so that a) it's clear to the reader of the Feature file who Tom is and b) to make sure the fixture data is still valid.
A reader of that scenario will immediately understand what's important about Tom (he's 16), and they don't have to continuously reference between the Scenario and Background. Other Scenarios can also use Tom and if they are interested in other aspects of his information (e.g. Address) then they can specify the relevant information Given there is a customer "Tom" who lives at 10 Downing Street.
Which approach is best depends how much of this data you've got. If it's a small number of fields across a couple of tables then put it in the Background, but once it gets to be 10+ fields or large numbers of tables (presumably we have many potential customers) then I'd suggest moving it outside the Feature file and just describing the relevant information in each Scenario.

Yes, you use a background, i.e. from https://github.com/cucumber/cucumber/wiki/Background
Background:
Given I have a data table "Employee"
| Field Name | Value |
| Name | "Tom" |
| Age | 16 |
Scenario: Some scenario 1
When ...
Scenario: Some scenario 2
Given I have another data table
| Field Name | Value |
| Brand | "Volvo" |
| City | "London" |
If ever you aren't sure I find http://www.specflow.org/documentation/Using-Gherkin-Language-in-SpecFlow/ a great resource

Related

MS Excel: How to list all column if the rows contain a given date?

My data looks like below. I have Groups that I share topics each day. We do this randomly based on need.
| | Topic 1 | Topic 2 | Topic 3 | Topic 4 | Topic 5 | Topic 6 | Topic 7 | Topic 8 | Topic 9 |
|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
| Group 1 | | 19-apr | 30-apr | | | | | | |
| Group 2 | 18-apr | 25-apr | | | | | | | |
| Group 3 | | | | | 19-apr | 30-apr | | | |
| Group 4 | 18-apr | 25-apr | | | | | | | |
| Group 5 | | | | | | | 19-apr | 30-apr | |
| Group 6 | | | 25-apr | | | | | | |
| Group 7 | 18-apr | 25-apr | | | | | | | |
For our metrics & analysis, we need a list of groups per date on a different sheet. We like to know which all groups were engaged a given day. Like below
Can somebody please help me how I can get this done with only using formulas and without macros?
I believe this can somehow be handled on Index Matching or look-ups.
You could definitely do this with macros. You can do something similar without macros; it may not be precisely what you were looking for because it will leave blank space where groups were not addressed.
Method 1
Here is the formula I used and a picture of the sheet it is in:
=IF(IFERROR(MATCH(L$4,$B6:$H6,0),FALSE),INDEX($B$5:$B$13,MATCH($K5,$B$5:$B$13,0),1),"")
The idea is that if you have absolute references alongside your list of groups per date, then you can use index and match to fill in that group's name, but only if Match finds that precise date code in that group's row from the previous table. If you place an equivalent formula in the first cell, you can drag it out to the rest of the array.
The formula I used is not the only way to do this, but if you know Index and Match, then it should make sense to you.
Method 2
A more convoluted method would be to use image references. With these, it is possible to make the report precisely what you asked for on a separate sheet.
Suppose you took Method 1 and separated each column out into a different table. Nearly the same formula inside the cells below the date heading, except that you enclose the heading reference in int() as shown below. Create one table for each of N dates, where N is the number of days you want to monitor at once. Then when you want the summary to show you different dates, you go to each table and change the heading, and filter out blanks.
formula:
=IF(IFERROR(MATCH(INT($L$2),$B4:$H4,1),FALSE),INDEX($B$4:$B$11,MATCH($K3,$B$4:$B$11,1),0),"")
The below image shows what I mean by one table for each date:
Then you insert an image. Doesn't matter what image; could be a screenshot of anything. Click on that image, then click into the formula bar. Then highlight the table column you want it to represent. Below is a screenshot of how to to that:
Now place that picture on its own sheet in the workbook. Place each date table on its own sheet in the workbook. The reason you do this is: if you filter a table, everything else overlapping the filtered rows outside the table will also be hidden. You move tables to separate sheets to prevent them from hiding each other.
Finally, arrange your pictures into the order you like, filter the blanks out of the tables, and your images will be exactly what you were looking for:
Again, this is a little convoluted because if you want the report to show you new date summaries, you would have to change the headings on every table. Then you would have to go to each table and refresh it's filter. This is where macros usually come in.
Assume range A1:J8 housed your Source table, and L1:P8 housed the Date/Group Output
1] In L2, copied across :
=IFERROR(1/(1/AGGREGATE(15,6,$B$2:$J$8/($B$2:$J$8>K$2),1)),"")
2] In L3, copied across to P3 and all copied down :
=IF(L$2="","",IFERROR(INDEX($A:$A,AGGREGATE(15,6,ROW($A$2:$A$8)/($B$2:$J$8=L$2),ROW(A1))),""))
You can use the following formula to get a list of dates from a table:
=IFERROR(AGGREGATE(15,6,($B$2:$J$8/($B$2:$J$8*(COUNTIF($A$15:A15,$B$2:$J$8)=0)))*$B$2:$J$8,1),"")
To get a list of groups by date, use the following:
=IFERROR(INDEX($A$1:$A$8,AGGREGATE(15,6,(1/(B$15=$B$1:$J$8))*ROW($B$1:$J$8),ROW(A1))),"")

EXCEL: SUMIFS criterion applied to a INDEX MATCH search equals a value

I've spent pretty much all day trying to figure this out. I've read so many threads on here and on various other sites. This is what I'm trying to do:
I've got the total sales output. It's large and the number of items on it varies depending on the time frame it's looked at. There is a major lack in the system where I cannot get the figures by region. That information is not stored in the system. The records only store the customer's name, the product information, number of units, price, and purchase date. I want to get the total number of each item sold by region so that I can compare item popularity across regions.
There are only about 50 customers, so it is feasible for me to create a separate sheet assigning a region to the customers.
So, I have three sheets:
Sheet 1: Sales
+-----------------------------------------------------+
|Customer Name | Product | Amount | Price | Date |
-------------------------------------------------------
| Joe's Fish | RT-01 | 7 | 5.45 | 2020/5/20 |
-------------------------------------------------------
| Joe's Fish | CB-23 | 17 | 0.55 | 2020/5/20 |
-------------------------------------------------------
| Mack's Bugs | RT-01 | 4 | 4.45 | 2020/4/20 |
-------------------------------------------------------
| Joe's Fish | VX-28 | 1 | 1.20 | 2020/5/13 |
-------------------------------------------------------
| Karen's \/ | RT-01 | 9 | 3.45 | 2020/3/20 |
+-----------------------------------------------------+
Sheet 2: Regions
+----------------------+
| Customer | Region |
------------------------
| Joe's Fish | NA |
------------------------
| Mack's Bugs | NA |
------------------------
| Karen's \/ | EU |
+----------------------+
And my results are going in Sheet 3:
+----------------------+
| | NA | EU |
------------------------
| RT-01 | 11 | 9 |
+----------------------+
So looking at the data I made up for this question, I want to compare the number of RW-01's sold in North America to those sold in Europe. I can do it if I add an INDEX MATCH column to the end of the sales sheet, but I would have to do that every time I update the sales information.
Is there some way to do a SUMIFS like:
SUMIFS(Sheet1!$D:$D,Sheet1!$A:$A,INDEX(Sheet2!$B:$B,MATCH(Sheet1!#Current A#,Sheet2!$A:$A))=Sheet3!$B2,Sheet1!$B:$B,Sheet3!$A3)
?
I think it's difficult to do it with a SUMIFS because the columns you're matching have to be ranges, but you can certainly do it with a SUMPRODUCT and COUNTIFS:
=SUMPRODUCT(Sheet1!$C$2:$C$10*(Sheet1!$B$2:$B$10=$A2)*COUNTIFS(Sheet2!$A$2:$A$5,Sheet1!$A$2:$A$10,Sheet2!$B$2:$B$5,B$1))
I don't recommend using full-column references because it could be slow.
BTW I was assuming that there were no duplicates in Sheet2 for a particular combination of customer and region - if there were, you could use
=SUMPRODUCT(Sheet1!$C$2:$C$10*(Sheet1!$B$2:$B$10=$A2)*
(COUNTIFS(Sheet2!$A$2:$A$5,Sheet1!$A$2:$A$10,Sheet2!$B$2:$B$5,B$1)>0))
EDIT
It is worth using a dynamic version of the formula, though it is not elegant:
=SUM(Sheet1!$C2:INDEX(Sheet1!$C:$C,MATCH(2,1/(Sheet1!$C:$C<>"")))*(Sheet1!$B2:INDEX(Sheet1!$B:$B,MATCH(2,1/(Sheet1!$B:$B<>"")))=$A2)*
(COUNTIFS(Sheet2!$A$2:INDEX(Sheet2!$A:$A,MATCH(2,1/(Sheet2!$A:$A<>""))),Sheet1!$A2:INDEX(Sheet1!$A:$A,MATCH(2,1/(Sheet1!$A:$A<>""))),Sheet2!$B$2:INDEX(Sheet2!$B:$B,MATCH(2,1/(Sheet2!$B:$B<>""))),B$1)>0))
As you would need to make the match in memory I don't think it's feasible in Excel, you'll have to use a vba dictionary.
On the other hand, if the number of columns is fixed in your sales sheet, you can just format as table and add your index match in F.
When updating the sales data delete all lines as of line 3 and copy paste the update value. Excel will automatically apply the index match on all rows.

Should all fields that are visible on a screen be validated in Gherkin?

We are creating Gherkin feature files for our application to create executable specifications. Currently we have files that look like this:
Given product <type> is found
When the product is clicked
Then detailed information on the product appears
And the field text has a value
And the field price has a value
And the field buy is available
We are wondering if this whole list of and keywords that validate if fields are visible on the screen is the way to go, or if we should shorten that to something like 'validate input'.
We have a similar case in that our service can return a lot of 10's of elements for each case that we could validate. We do not validate every element for each interaction, we only test the elements that are relevant to the test case.
To make it easier to maintain and switch which elements we are using, we use scenario outlines and tables of examples.
Scenario Outline: PO Boxes correctly located
When we search in the USA for "<Input>"
Then the address contains
| Label | Text |
| PO Box | <PoBox> |
| City name | <CityName> |
| State code | <StateCode> |
| ZIP Code | <ZipCode> |
| +4 code | <ZipPlus4> |
Examples:
| ID | Input | PoBox | CityName | StateCode | ZipCode |
| 01 | PO Box 123, 12345 | PO Box 123 | Boston | MA | 12345 |
| 02 | PO Box 321, Whitefish | PO Box 123 | Whitefish | MN | 54321 |
By doing it this way, we have a generic step "the address contains" that uses the 'Label' and 'Text' to test the individual elements. It is a neat and tidy way to test a lot of potential combinations - but it probably depends on your individual use case - how important all of the fields are.
You only need to validate the ones that provide business value, which is probably all of them. I would avoid using tech terms like "field" because it isn't related to a behavior. Al Mills is right on for using the tables.
I'd word it like this:
Scenario Outline: Review product details
Given I find the product <Type>
When I select the product
Then detailed information on the product appears including
| Description | <Description> |
| Price | <Price> |
And I can buy the product
Examples:
| Type | Description | Price |
| Hose | Rubber Hose | 31.99 |
| Sprinkler | Rotating Sprinker | 12.99 |
The words I chose are behaviors or whats, not technical implementations or hows.

How to present/organize repetitive data contained in cells in excel?

Would there be a better way to present/organize repetitive data contained in cells in excel? I've provided a set of sample data below (I can't show my actual records for confidentiality reasons) just so that you guys could visualize the data.
Country | Type | Company | Department | Project | Currency | Amount
USA | Restaurant | Bob's Barbeque | Sanitation | Monthly Sanitation | Records Database | USD | 100
USA | Restaurant | Bob's Barbeque | Human Resources | Staff Timesheet USD | 203
USA | Restaurant | Bob's Barbeque | Human Resources | Email Portal for Employees | USD | 259
USA | Manufacturing | Duff Autos | Research and Development | Inventory System| USD | 234
USA | Manufacturing | Duff Autos | Sales | Inventory System | USD | 654
China | Manufacturing | Weihua Electronics | Research and Development | Inventory System | RMB | 102
China | Government | Department of Tourism | Advertisement | Billboard Design and Deployment Web Portal | RMB | 2044
As you can see, it contains a lot of repetitive data like the "Country" and the "Type". It may look organized as it is but it looks cluttered as the data multiplies. I've thought this thoroughly over the past few days but I still can't figure out a way to do organize it such that it would look pretty.
I've tried Excel's filter functionality, sorting, and others but I still think it looks cluttered. Any suggestions? Thanks!
Pivot the data, then you can open and close the sections as you please. I just did it on your sample, then in the resulting pivot tick all the fields, drag the amount one from row labels to values then click on the little down arrow and choose value field settings.
Select Sum and press OK.
the + and - on the data in the pivot allows you to fold open and close the fields to make it look however you like.

How should I name my steps in a scenario outline

I've got a handful of specflow tests that look something like this:
Scenario: Person is new and needs an email
Given a person
And the person does not exist in the repository
When I run the new user batch job
Then the person should be sent an email
Scenario: Person is not new and needs an email
Given a person
And the person does exist in the repository
When I run the new user batch job
Then the person should not be sent an email
Except instead of just 2 scenarios, I've got 10 very similar scenarios, all with the type of steps so I want to use a "Scenario Outline". Unfortunately, I'm having a really hard time coming up with a readable way to re-write my steps.
Currently, I've come up with this but looks clunky:
Scenario: Email batch job is run
Given a person
And the person '<personDoes/NotExist>' exist in the repository
When I run the new user batch job
Then the person '<personShould/NotGetEmail>' be sent an email
Examples:
| !notes | personDoes/NotExist | personShould/NotGetEmail |
| Exists | does not | should |
| No Exist | does | should not |
I also considered this, and while it is cleaner it doesn't convey meaning nearly as well
Scenario: Email batch job is run
Given a person
And the person does exist in the repository (is '<personExist>')
When I run the new user batch job
Then the person should be sent an email (is '<sendEmail>')
Examples:
| !notes | personExist | sendEmail |
| Exists | false | true |
| No Exist | does | false |
Does anybody have a better way of parameterizing concepts like "does", "does not", "should", "should not", "has", "has not"? At this point, I'm thinking about leaving the everything as a different scenario because it is more readable.
Here is what I've done in the past:
Given these people exist in the external system
| Id | First Name | Last Name | Email |
| 1 | John | Galt | x |
| 2 | Howard | Roark | y |
And the following people exist in the account repository
| Id | External Id | First Name | Last Name |
| 45 | 1 | John | Galt |
When I run the new user batch job
Then the following people should exist in the account repository
| External Id | First Name | Last Name | Email |
| 1 | John | Galt | x |
| 2 | Howard | Roark | y |
And the following accounts should have been sent an email
| External Id | Email |
| 2 | y |
You can use the table.CreateSet() and table.CreateSet() helper methods in SpecFlow to quickly turn the tables into data for your fake external system repository and your account table in the database.
Then you can use table.CompareToSet(accountRepository.GetAccounts() to compare the table in your "Then" clause to the records in your database.
The neat thing is, all of the steps you wrote are reusable for multiple situations. All you do is change the data in the tables, and SpecFlow writes the tests for you.
Hope that helps!
Maybe you should split them into two scenarios
Scenario Outline: User exists in the repository
Given a person
| Field | Value |
| First | <first> |
| Last | <last> |
And the person exists in the repository
When the user attempts to register
Then the person should be sent an email
Examples:
| first | last |
| Bob | Smith |
| Sarah | Jane |
And then another scenario for the opposite. This keeps the scenario meaning very clear. If your common steps are worded genericly you can reuse them. I also try to come from the approach of the user

Resources