Skip to content

[API] Demo of REST API test automation #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Compiled class file
*.class
target/

# Log file
*.log
Expand Down
4 changes: 2 additions & 2 deletions aquality-selenium-template-cucumber/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<dependency>
<groupId>com.github.aquality-automation</groupId>
<artifactId>aquality-tracking-cucumber5-jvm</artifactId>
<version>1.3.0</version>
<version>1.3.1</version>
</dependency>

<dependency>
Expand All @@ -51,7 +51,7 @@
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-cucumber5-jvm</artifactId>
<version>2.13.3</version>
<version>2.19.0</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@API @demo
Feature: Demo API

Scenario: GET user info
When I send GET '/users/aqualityAutomation' request to github with saving the 'response'
Then the status code of the 'response' is '200'
And the 'response' matches json schema 'users/UserResponse'
And the 'login' is 'aqualityAutomation' in the 'response'
And the 'type' is 'User' in the 'response'
And the 'response' time is less than or equal to 5 seconds
When I save the user from the 'response' as 'user1'
And I send GET '/users/aquality-automation' request to github with saving the 'response2'
And I save the user from the 'response2' as 'user2'
Then User 'user1' is different from the user 'user2'

Scenario: GET organization info
When I send GET '/users/aquality-automation' request to github with saving the 'response'
Then the status code of the 'response' is '200'
And the 'response' matches json schema 'users/UserResponse'
And the 'login' is 'aquality-automation' in the 'response'
And the 'type' is 'Organization' in the 'response'
And the 'id' is 50261201 in the 'response'
And the 'response' time is less than or equal to 5 seconds

Scenario: GET users info
When I send GET '/users' request to github with saving the 'users response'
Then the status code of the 'users response' is '200'
And the 'users response' matches json schema 'users/UsersResponse'
And the 'users response' time is less than or equal to 5 seconds
And the '.' array has size less than or equal to 30 in the 'users response'
And the '.' array is ordered ascending by 'id' in the 'users response'
When I extract the '[0].url' from the 'users response' with saving it as 'user URL'
And I send GET request to github endpoint saved as 'user URL' with saving the 'response'
Then the status code of the 'response' is '200'
And the 'response' matches json schema 'users/UserResponse'
And the 'url' has the value saved as 'user URL' in the 'response'
And the 'response' time is less than or equal to 5 seconds
Original file line number Diff line number Diff line change
@@ -1,14 +1,40 @@
package aquality.selenium.template.cucumber.hooks;

import aquality.selenium.browser.AqualityServices;
import aquality.selenium.template.utilities.IScreenshotProvider;
import io.cucumber.java.After;
import io.cucumber.java.Scenario;
import org.openqa.selenium.logging.LogType;

import javax.inject.Inject;
import java.nio.charset.StandardCharsets;

import static aquality.selenium.browser.AqualityServices.getBrowser;

public class BrowserHooks {

private final IScreenshotProvider screenshotProvider;

@Inject
public BrowserHooks(IScreenshotProvider screenshotProvider) {
this.screenshotProvider = screenshotProvider;
}

@After(order = 1)
public void attachArtifacts(Scenario scenario) {
if (AqualityServices.isBrowserStarted()) {
scenario.attach(screenshotProvider.takeScreenshot(), "image/png", "screenshot.png");
scenario.attach(getBrowser().getDriver().getPageSource().getBytes(StandardCharsets.UTF_8),
"text/html", "source.html");
scenario.attach(getBrowser().getLogs(LogType.BROWSER).toJson().toString().getBytes(StandardCharsets.UTF_8),
"text/json", "console.log");
}
}

@After(order = 0)
public void closeBrowser() {
if (AqualityServices.isBrowserStarted()) {
AqualityServices.getBrowser().quit();
getBrowser().quit();
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package aquality.selenium.template.cucumber.stepdefinitions.api.common;

import aquality.selenium.template.cucumber.utilities.ScenarioContext;
import aquality.selenium.template.utilities.FileHelper;
import com.fasterxml.jackson.databind.JsonNode;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import io.qameta.allure.Allure;
import io.restassured.response.Response;
import lombok.SneakyThrows;
import org.testng.Assert;

import javax.inject.Inject;
import java.io.FileInputStream;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static aquality.selenium.template.cucumber.utilities.SortingUtilities.sort;
import static aquality.selenium.template.cucumber.utilities.SortingUtilities.sortAsNumbers;
import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
import static java.lang.String.format;
import static org.hamcrest.Matchers.*;

public class ResponseSteps {
private static final String SCHEMA_PATH_TEMPLATE = "jsonschemas/%s.json";
private final ScenarioContext scenarioContext;

@Inject
public ResponseSteps(ScenarioContext scenarioContext) {
this.scenarioContext = scenarioContext;
}

@Then("the status code of the '{response}' is '{int}'")
public void statusCodeOfResponseIs(Response response, int statusCode) {
response.then().statusCode(statusCode);
}

@Then("the {string} has the value saved as '{contextKey}' in the '{response}'")
@Then("the {string} is {string} in the '{response}'")
@Then("the {string} is {int} in the '{response}'")
public void fieldInResponseIs(String fieldName, Object expectedValue, Response response) {
response.then().body(fieldName, equalTo(expectedValue));
}

@Then("the {string} array has size less than or equal to {int} in the '{response}'")
public void fieldArrayInResponseHasSizeLessThanOrEqualTo(String fieldName, int maxSize, Response response) {
response.then().body(fieldName, hasSize(lessThanOrEqualTo(maxSize)));
}

@Then("the {string} array is ordered {isAscendingOrder} by {string} in the '{response}'")
public void fieldArrayInResponseIsSortedBy(String path, boolean isAscendingOrder, String fieldName, Response response) {
List<JsonNode> nodeList = response.then().extract().body().jsonPath().getList(path, JsonNode.class);
if (nodeList == null || nodeList.isEmpty()) {
throw new IllegalArgumentException("Cannot check order on null or empty collection");
}
List<String> actualOrder = nodeList.stream().map(node -> node.get(fieldName).toString()).collect(Collectors.toList());
List<String> expectedOrder = nodeList.get(0).get(fieldName).isNumber()
? sortAsNumbers(actualOrder, isAscendingOrder)
: sort(actualOrder, isAscendingOrder);
Assert.assertEquals(actualOrder, expectedOrder,
format("%s items must be sorted by %s in correct order. %nExpected:\t %s %n Actual:\t %s%n",
path, fieldName, expectedOrder, actualOrder));
}

@Then("the '{response}' matches json schema {string}")
@SneakyThrows
public void responseMatchesJsonSchema(Response response, String schemaName) {
String pathToSchema = format(SCHEMA_PATH_TEMPLATE, schemaName);
Allure.addAttachment("json schema",
new FileInputStream(FileHelper.getResourceFileByName(pathToSchema)));
response.then().body(matchesJsonSchemaInClasspath(pathToSchema));
}

@When("I extract the {string} from the '{response}' with saving it as {string}")
public void extractAndSave(String path, Response response, String contextKey) {
scenarioContext.add(contextKey, response.then().extract().path(path));
}

@Then("the '{response}' time is less than or equal to {long} seconds")
public void responseTimeIsLessOrEqualTo(Response response, long seconds) {
response.then().time(lessThanOrEqualTo(seconds), TimeUnit.SECONDS);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package aquality.selenium.template.cucumber.stepdefinitions.api.github;

import aquality.selenium.template.cucumber.utilities.ScenarioContext;
import io.cucumber.java.en.When;
import io.restassured.response.Response;

import javax.inject.Inject;

import static aquality.selenium.template.utilities.RequestSpecifications.commonGiven;

public class RequestSteps {
private final ScenarioContext scenarioContext;

@Inject
public RequestSteps(ScenarioContext scenarioContext) {
this.scenarioContext = scenarioContext;
}

@When("I send GET '{endpoint}' request to github with saving the '{responseKey}'")
@When("I send GET request to github endpoint saved as '{contextKey}' with saving the '{responseKey}'")
public void sendGetRequestToGitHubAndSaveResponse(String endpoint, String contextKey) {
Response response = commonGiven().when().get(endpoint);
scenarioContext.add(contextKey, response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package aquality.selenium.template.cucumber.stepdefinitions.api.github;

import aquality.selenium.template.cucumber.utilities.ScenarioContext;
import aquality.selenium.template.models.User;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import io.restassured.response.Response;
import org.testng.Assert;

import javax.inject.Inject;

public class UserSteps {
private final ScenarioContext scenarioContext;

@Inject
public UserSteps(ScenarioContext scenarioContext) {
this.scenarioContext = scenarioContext;
}

@When("I save the user from the '{response}' as {string}")
public void saveTheUserFromTheResponse(Response response, String key) {
scenarioContext.add(key, response.as(User.class));
}

@Then("User '{contextKey}' is different from the user '{contextKey}'")
public void userUserIsDifferentFromTheUserUser(User user1, User user2) {
Assert.assertNotEquals(user1, user2, "Users should not be equal");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package aquality.selenium.template.cucumber.stepdefinitions.data;

import aquality.selenium.template.cucumber.utilities.ScenarioContext;
import io.cucumber.java.ParameterType;
import io.qameta.allure.Allure;
import io.qameta.allure.model.Parameter;
import io.qameta.allure.util.ResultsUtils;

import javax.inject.Inject;
import java.util.List;

public class SavedDataParameterTypes {
private final ScenarioContext scenarioContext;

@Inject
public SavedDataParameterTypes(ScenarioContext scenarioContext) {
this.scenarioContext = scenarioContext;
}

@ParameterType(".*")
public <T> T contextKey(String key) {
if (!scenarioContext.hasSaved(key)) {
throw new IllegalArgumentException(String.format("No value is stored in the context with name [%s]", key));
}
T value = scenarioContext.get(key);
Parameter allureParameter = ResultsUtils.createParameter(key, value);
Allure.getLifecycle().updateStep(stepResult -> {
List<Parameter> parameters = stepResult.getParameters();
parameters.add(allureParameter);
stepResult.setParameters(parameters);
});
return value;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package aquality.selenium.template.cucumber.stepdefinitions;
package aquality.selenium.template.cucumber.stepdefinitions.data;

import aquality.selenium.template.cucumber.utilities.ScenarioContext;
import io.cucumber.java.en.Then;
Expand All @@ -8,30 +8,26 @@

import static org.testng.Assert.assertEquals;

public class ScenarioContextDemoSteps {

public class ScenarioContextSteps {
private final ScenarioContext scenarioContext;

@Inject
public ScenarioContextDemoSteps(ScenarioContext scenarioContext) {
public ScenarioContextSteps(ScenarioContext scenarioContext) {
this.scenarioContext = scenarioContext;
}

@When("I store '{int}' as '{}'")
@When("I store '{int}' as {string}")
public void storeValue(int value, String key) {
scenarioContext.add(key, value);
}

@When("I add '{}' to '{}' and store it as '{}'")
public void addAndStore(String key1, String key2, String resultKey) {
int value1 = scenarioContext.get(key1);
int value2 = scenarioContext.get(key2);
@When("I add '{contextKey}' to '{contextKey}' and store it as {string}")
public void addAndStore(int value1, int value2, String resultKey) {
scenarioContext.add(resultKey, value1 + value2);
}

@Then("'{}' should be equal to '{int}'")
public void theResultShouldBeEqualTo(String resultKey, int expectedResult) {
int actualResult = scenarioContext.get(resultKey);
@Then("'{contextKey}' should be equal to '{int}'")
public void theResultShouldBeEqualTo(int actualResult, int expectedResult) {
assertEquals(actualResult, expectedResult, "The result is incorrect");
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package aquality.selenium.template.cucumber.stepdefinitions;
package aquality.selenium.template.cucumber.stepdefinitions.ui;

import aquality.selenium.template.forms.pages.ContactUsPage;
import aquality.selenium.template.models.ContactUsInfo;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package aquality.selenium.template.cucumber.stepdefinitions;
package aquality.selenium.template.cucumber.stepdefinitions.ui;

import aquality.selenium.browser.AqualityServices;
import aquality.selenium.template.configuration.Configuration;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package aquality.selenium.template.cucumber.transformations;

import io.cucumber.java.ParameterType;

import java.util.Arrays;
import java.util.List;

public class SortingParameterTypes {
@ParameterType("alphabetically|A-Z|Z-A|ascending|descending|asc|desc")
public boolean isAscendingOrder(String order) {
List<String> ascOrder = Arrays.asList("alphabetically", "A-Z", "ascending", "asc");
return ascOrder.contains(order);
}
}
Loading