3

I'm learning to use Cucumber and have a Scenario as follows:

Given a standard party
And a flight from "UK" to "Europe"
When I search
And I receive search results
Then I can book my holiday

In Step Two's Step Definition, I query a database using JDBI and receive a dataset that I can iterate over and use as search data for Step 3 in the API I am sending requests to. It's is quite common not to receive any results on the first 2 or 3 rows queried. This is not useful to me because the scenario is not really testing for whether results are returned (although that is clearly a valid scenario).

What I really want is to get results back and then assert that I can book - within that Step Def are checked for correct price etc....

So, if

And I receive search results

equates to false, how can I return to

And a flight from.....

...so that I can try the next row of data.

My query then is in the context of Cucumber Step Definitions, what it is the best/ idiomatic way to do this, given that the for each loop is in the earlier step?

Without exposing too much of the Step Def code, is there a common convention for retrying in such cases? I can't simply call the underlying search method again because I have many search Step Def methods all glued to steps and the "And I receive..." in which I receive a response object has no concept of the preceding Step.

And a flight from "UK" to "Europe":

 @And("^a flight from \"([^\"]*)\" to \"([^\"]*)\"$")
public void aFlightFromContinentOneToContinentTwo(String continentOne, String continentTwo) {

    airportsAndDates = searchWorld.airportsAndDates();

    supplierCode = supplierCredentials.getSupplierCode();
    ReposMasterConnection rm = new ReposMasterConnection();

    origins = getContinentOne(continentOne);
    destinations = getContinentTwo(continentTwo);

    rm.initialiseDatabaseConnection();

    List<FlightDataBean> dataSet = rm.lastFlightBookedResultsByRegion(supplierCode, origins, destinations);

    for (FlightDataBean i : dataSet) {
        String startDate = i.getStartDate();
        String endDate = i.getEndDate();
        String origin = i.getOrigin();
        String destination = i.getDestination();

        String start = getStartDate(startDate);
        String end = getEndDate(endDate);

        if (startDate != null) {

            DateRange dateRange = new DateRange()
                    .withStartDate(start)
                    .withEndDate(end);

            airportsAndDates.
                    withDepartureDateRange(dateRange).
                    withDepartureAirports(departureAirportList(origin)).
                    withDestinationChoice(destinationChoice(destinationAirportList(destination)));
        }
    }
}

When I search...//another Stepdef which posts the request

Then I receive search results:

@Then("^I receive search results$")
public void iReceiveSearchResults() {

    response = given().contentType(ContentType.XML).body(packageAvailRequest).when().post().then().extract().response();
    String status = requestWorld.getAppRequest().getAvailabilityResponseObject().getSearchResponseStatus(response);

    requestWorld.getAppRequest().getAvailabilityResponseObject().setResponse(response);

    errorMessage = requestWorld.getAppRequest().getFirstBasketResponseObject().getError(response);
    warningMessage = requestWorld.geAppRequest().getFirstBasketResponseObject().getWarning(response);

    if (errorMessage != null && !errorMessage.isEmpty()) {
        scenario.write("Error: " + errorMessage);
    }
    if (warningMessage != null && !warningMessage.isEmpty()) {
        scenario.write("Warning: " + warningMessage);
    }

    assertEquals("true", status);
}
  • Am I right that you'd like to take some other pair instead of "uk"/"europe" if there are no flights? If yes, where do you sore your from ".." to ".." data? – Alexey R. Sep 18 '18 at 9:41
1

I would add a method in step two to iterate through the data to find viable set, only failing if at the end of the iteration, no viable data had appeared.

  • I didn't word the question very well but thank you for the input. Yes, this is the ideal. But, my 'When' step is posting a request to include the viable dataset so I only get a response with a true or false in the When step. Bringing the request into the second 'And' removes the state transition into an And. I should probably conflate steps 2 and 3 into a single When – Steerpike Sep 19 '18 at 14:06
1

Two thoughts.

Shouldn't your db query be specific enough to only return records that meet the criteria?

Second, if you are not sure what is in the data, you should create the data yourself. If you inserted the record, you can count on it being there.

1

Your second step is part of the "Given" section, that means that there should be a flight from "UK" to "Europe" to proceed with the when section of the scenario.

The way you make sure this "given" is fulfilled depends on your context. When you have the full control over the environment you're running your tests against, you might insert the flights into the database at this step.

When you cannot modify the flights data or you run your test suite on a shared environment and do not want interference with others, you can stick to some routes where there are always flights (by convention or domain knowledge). In such case you might want to add a check that the flight indeed exists, failing the test case early in the given section should the convention deviate.

In general, try to keep steps as much isolated from each other as possible, and avoid state whenever possible. This will make the steps more reusable and less brittle.

  • Thank you, good advice and I agree with all your points. – Steerpike Sep 21 '18 at 7:33
  • I've exposed the relevant Stepdefs to try and give context. I might pull back 500 data rows. In theory, I should get a hit first time, because it's a copy of a live DB. But, sometimes, I don't and usually in this case, I have to move to row 2 or 3 before I get lucky. So, I think I need a specific Iterator that can be instantiated in " And a flight from "uk" to "europe" " step and then incremented in the "Then I receive search results" step IF no results are found - cos I only make the POST within this step, so this is the only point where I know if I get results. – Steerpike Sep 21 '18 at 7:40
1

There isn't because you shouldn't

Steps are designed to go beginning to end. It's not trying to do logic. Each scenario is a distinct test.

Now, you can write scenarios that repeat with new parameters, but the steps need to take that into account. You change not only the data entered but how the results are handled. The input source (table, XML, JSON, Excel, etc) would have not just a column for "UK" and "Europe", but also a THEN clause such as "I can book my holiday" or "I stay home".

What you're looking for is called "Scenario Outline"


The problem you're having is that your really testing the wrong thing. Your test isn't testing what happens when you have a flight, but testing IF you have a flight.

In other words, you're really testing for the existence of a flight first, but that's not part of your scenario. Since you can't guarantee the flight exists, you can't define exactly how to proceed.

By saying that a flight from "UK" to "Europe" is a Given, you're promising that as a starting point. But since you can't always know for sure, it's not really a Given.

  • I'm not looking for a Scenario Outline. I want to run the Scenario only once. My issue is that I frequently get no results back from my search. Yes, in an ideal world, I would inject the data myself to ensure results every time but I'm dealing with a system that is flaky. So, I wish to iterate over flight data and use them in my search until I receive a hit so I can move to the meat of what I want to test - the costing and booking – Steerpike Sep 21 '18 at 7:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.