Creating our API Test Scenarios
Get Recent Tweets from our Home Timeline
Creating the Cucumber Scenario
When creating our BDD scenarios, we want to keep them simple and write them in a declarative style, sticking simply to the business logic, rather than writing them in an imperative style. A great article on this can be found at http://itsadeliverything.com/declarative-vs-imperative-gherkin-scenarios-for-cucumber.
- Open up the ‘BaseApiScenarios.feature’ file and add the following test scenario. We can also add a Cucumber tag of @Api either above the ‘Scenario’ (to apply to the specific scenario) or above the ‘Feature’ (to apply to all scenarios within the feature), which will set the BaseUri for us before we run our test scenario/s…
@Api Feature: Test Twitter Tweets Scenario: 01. Get recent tweets from our Home Timeline Given a Twitter user posts a tweet of "Hello World! This is a test tweet" When they retrieve the results of "/home_timeline.json" Then the most recent tweet in the Home Timeline is "Hello World! This is a test tweet"
You should notice that the ‘Given’, ‘When’ and ‘Then’ steps are highlighted. This is because they do not yet have any step definitions attached to them.
Creating the Undefined Step Definitions
- Open up CMD / Terminal and ‘cd’ (change directory) in to the root of your Maven or Gradle project
- Enter in (without the $)
$ mvn install
or enter (without the $)…
$ gradle build
- Scroll up in the CMD / Terminal and you will be able to see the undefined step definitions
- Copy and paste the undefined ‘Given’ step definition in to the ‘BaseApiScenariosSteps’ class, it should end up looking like below…
package steps; import cucumber.api.PendingException; import io.cucumber.java.en.Given; public class BaseApiScenariosSteps { @Given("^a Twitter user posts a tweet of \"([^\"]*)\"$") public void aTwitterUserPostsATweetOf(String arg1) throws Throwable { // Write code here that turns the phrase above into concrete actions throw new PendingException(); } }
- Copy and paste the undefined ‘When’ step definition in to the ‘BaseApiScenariosSteps’ class
- Copy and paste the undefined ‘Then’ step definition in to the ‘BaseApiScenariosSteps’ class
The ‘BaseApiScenariosSteps’ step definition class file should end up looking like below…package steps; import cucumber.api.PendingException; import io.cucumber.java.en.Given; import io.cucumber.java.en.When; import io.cucumber.java.en.Then; public class BaseApiScenariosSteps { @Given("^a Twitter user posts a tweet of \"([^\"]*)\"$") public void aTwitterUserPostsATweetOf(String arg1) throws Throwable { // Write code here that turns the phrase above into concrete actions throw new PendingException(); } @When("^they retrieve the resource of \"([^\"]*)\"$") public void theyRetrieveTheResourceOf(String arg1) throws Throwable { // Write code here that turns the phrase above into concrete actions throw new PendingException(); } @Then("^the most recent tweet in the Home Timeline is \"([^\"]*\"$") public void theMostRecentTweetInTheHomeTimelineIs(String arg1) throws Throwable { // Write code here that turns the phrase above into concrete actions throw new PendingException(); } }
Creating the Test Methods
We will now create test methods in Java for each of our steps, in order to define them ?
postTweet() Method
- Add the following postTweet() method, like below, to post a tweet (from a String argument passed from our Cucumber step), using the account we have already authenticated with…
public static void postTweet(String message) { response = authTwitter().queryParam("status", message) .post("/update.json"); }
In the above method, we have called the ‘authTwitter()’ method, which returns the ‘httpRequest‘ RequestSpecification instance variable, which authenticates us. Having then been authenticated, we add a query parameter which passes in the message we want to tweet as our status, which we then post to the ‘/update.json’ resource.
getRequest() Method
- Open up the ‘BaseApiTests’ class in the ‘apis’ package and add an import for the following library which will be used to check the content type of our response is JSON in our ‘getRequest()’ method that we will create next…
import io.restassured.http.ContentType;
- Add the following getRequest() method, like below, to get a list of the most recent tweets posted by the user we are authenticated with…
public static void getRequest(String apiResource) { response = authTwitter().get(apiResource); response.then().assertThat().statusCode(200) .and().contentType(ContentType.JSON); strResponse = response.asString(); }
In the above method, we set our response to what is retrieved when we have authenticated (via the ‘httpRequest’ instance variable that is returned when we call the ‘authTwitter()’ method) and gotten the response of the ‘apiResource’ we have passed in from our Cucumber step (e.g. ‘/home_timeline.json‘). We then assert that the response returns a status code of 200 (OK) and that the content type is indeed JSON. Finally, we set the ‘strResponse’ instance variable to the string value of the response.
The above method can be re-used for any GET request we need to make in Twitter, due to the fact that we pass a String argument for our API resource 🙂
assertKeyValue() Method
- Open up the ‘BaseApiTests’ class in the ‘apis’ package and add an import for the following library which will be used to access the ‘from’ method, which allows us to move to a certain point in our string response (e.g. a certain JSON path), which we can use in our ‘assertKeyValue()’ method. We also want to import the TestNG library so we can use its Assert methods…
import static io.restassured.path.json.JsonPath.from; import org.testng.Assert;
- Add the following assertKeyValue() method, like below, to assert that a given Key in the JSON response, contains a given value…
public static void assertKeyValue(String keyValue, String passedValue) { keyValue = from(strResponse).get(keyValue); Assert.assertEquals(keyValue, passedValue); }
In the above method, we use the ‘from’ method of RestAssured to get the value of the given ‘keyValue’ key that we pass in to the method, and set the String ‘keyValue‘ variable to that key’s value. We then assert that the value gotten for that key is the same as the value we expect (the one we passed down from our Cucumber step). Again, this method can be re-used to assert that any given key has any given value, both passed in from our Cucumber step.
Connecting the Step Definitions to the Test methods
We will now call the test methods from their appropriate step definitions.
- Open up the ‘BaseApiScenariosSteps’ step definition class file and edit the undefined step definitions so that they are defined and call their appropriate methods. Also note how I’ve renamed the arguments that we pass in from our Cucumber steps, to something more suitable, so that it is easier to read. I’ve also removed the unused import of PendingException, as our step definitions are now defined. The whole thing should look like below when all the steps are defined…
package steps; import apis.BaseApiTests; import io.cucumber.java.en.Given; import io.cucumber.java.en.When; import io.cucumber.java.en.Then; public class BaseApiScenariosSteps { @Given("^a Twitter user posts a tweet of \"([^\"]*)\"$") public void aTwitterUserPostsATweetOf(String message) { BaseApiTests.postTweet(message); } @When("^I retrieve the resource of \"([^\"]*)\"$") public void theyRetrieveTheResourceOf(String apiResource) { BaseApiTests.getRequest(apiResource); } @Then("^the most recent tweet in the Home Timeline is \"([^\"]*\"$") public void theMostRecentTweetInTheHomeTimelineIs(String value) { BaseApiTests.assertKeyValue("[0].text", value); } }
Running the Test Scenario
- Go back to the cucumber scenario, right-click it and try to run it. CONGRATULATIONS! YOU HAVE JUST RUN YOUR FIRST PASSING API TEST!
If you login to your twitter account, you can see that the tweet was posted successfully.(Also worth noting is that Twitter has a rate limit applied to how many tweets you can post in a set amount of time, to prevent spam etc. If you don’t see your tweet posted because you’ve been debugging and testing a while, simply change the text in your tweet message to something else, as Twitter doesn’t like the same tweet being sent out in a row. If that still doesn’t work, then regenerate your Consumer Key and Secret again).