Search by labels

Monday, February 15, 2021

Test Automation for REST & GraphQL APIs

Backend test automation, the middle layer of the Testing Pyramid, is an area that no efficient test automation strategy should overlook. It's quick, it's stable and luckily with the use of specific Java libraries, it's also easy to implement.

This article will present a Backend test automation framework built with JUnit 5, REST Assured and a specific library for GraphQL file manipulation.

Project structure and setup

As it is a Maven project, all the needed libraries can be easily imported through the POM file.

Two helper classes are also needed:

  •  one for preparing the GraphQL payload, that can be checked here and is shown below:

public static String prepareGraphqlPayload(Map<String, String> variables, String queryFileLocation) {
        File file = new File(queryFileLocation);
        ObjectNode objectNode = new ObjectMapper().createObjectNode();
        for (Map.Entry<String, String> entry : variables.entrySet()) {
            objectNode.put(entry.getKey(), entry.getValue());
        }
        String graphqlPayload = null;
        try {
            graphqlPayload = GraphqlTemplate.parseGraphql(file, objectNode);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return graphqlPayload;
    }

  • one for preparing the JSON object that can be checked here and is shown below:

public static JSONObject buildJsonObject(String location) {
        JSONParser parser = new JSONParser();
        JSONObject jsonObject = null;
        try {
            jsonObject = (JSONObject) parser.parse(new FileReader(location));
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

Specifying the APIs and test data

The APIs under test will be:

As test data, we need:

  • for the GraphQL API:
  1. the Pokemon GraphQL query file
  2. the list of Pokemon and their attributes as a .csv file


Writing the test scenarios

  • The GraphQL test class can be checked here and the main test is detailed below:

@ParameterizedTest
@CsvFileSource(resources = "/graphql/pokemons.csv", numLinesToSkip = 1)
void testGraphQL(String id, String pokemonName, String weight, String height) {
        RestAssured.baseURI = "https://pokeapi-graphiql.herokuapp.com";
        Map<String, String> variables = new HashMap<>();
        variables.put("number", id);
        String graphqlPayload = GraphqlUtil.prepareGraphqlPayload(variables, "src/test/resources/graphql/pokemon.graphql");
        given().log().body()
                .contentType(ContentType.JSON)
                .body(graphqlPayload)
                .post("/graphql")
                .then()
                .statusCode(200)
                .body("data.pokemon.name", equalTo(pokemonName))
                .body("data.pokemon.weight", equalTo(weight))
                .body("data.pokemon.height", equalTo(height));
        logger.info("GraphQL request successful for " + pokemonName);
    }

Although a test scenario for a GraphQL API, the REST Assured library can be easily used and we can take advantage of its straightforward features for making the initial setup, executing the request and verifying the returned data.

  • The REST test class can be checked here and the initialisation step and first test case are detailed below:

    @BeforeAll
    static void initialize() {
        RestAssured.baseURI = "https://petstore.swagger.io/v2";
        petCreatedJson = buildJsonObject("src/test/resources/rest/pet_created.json");
        petUpdatedJson = buildJsonObject("src/test/resources/rest/pet_updated.json");
    }

    @Test
    @Order(1)
    void testPetCreation() {
        given().log().uri()
                .contentType(ContentType.JSON)
                .body(petCreatedJson)
                .post("/pet")
                .then()
                .statusCode(200).extract().response()
                .then().body("name", equalTo(petCreatedJson.get("name")));
        logger.info("Pet entry with id " + petCreatedJson.get("id") + " and name " + petCreatedJson.get("name") + " successfully CREATED");
    }

The @BeforeAll step handles the initial setup, setting the baseURI and building the two JSON objects that will be used in testing.

Because this test class was designed to verify the entire flow, with @Order used to guarantee the correct timing of the test scenarios, the execution flow will be the following:

  1. Create the pet entry
  2. Verify the entry with correct info was created
  3. Update the pet entry
  4. Verify the entry was updated with the correct info
  5. Delete the pet entry
  6. Verify the entry was deleted
Thus, this test suite can run independently each time and does not rely on previously available data, nor does it spam the database after each execution.

Sunday, February 7, 2021

Fast-Forward Frontend Test Automation with Selenide

Tired of having to write a lot of code (the Gherkin feature file, the Runner class, the Steps and the Page Object classes) just for a single Frontend test?

Also, you don't need the detailed and verbose reports offered by automation libraries like Cucumber or Serenity?

Then maybe Selenide is the solution you are looking for.

Selenide is a Selenium WebDriver wrapper, it builds on this time-tested library and comes with new and powerful methods that can be checked here.

This article will present a sample frontend test automation framework built with Selenide and JUnit 5. It can be checked here and let's go step by step into its' structure.

1. Setting up Selenide and JUnit 5

As it is a Maven project, simply add the needed dependencies in the pom file:

<dependency>
<groupId>com.codeborne</groupId>
<artifactId>selenide</artifactId>
<version>${selenide.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>

2. Define the test data

The showcased test is for a demo contact form so the test data can be easily centralised in a .csv file, thus taking advantage of the very useful @ParametrizedTest feature from JUnit 5.

3. Implement the test

Having JUnit in place, we can use its' annotations to best structure the test for the above contact form, thus:

  • We will open the webpage in the @BeforeEach step:
@BeforeEach 
public void loadWebpage() { 
 open("http://automationpractice.com/index.php?controller=contact"); 
     }
  • Implement the actual test steps in a @ParametrizedTest using as test data the above .csv file:
@ParameterizedTest
@CsvFileSource(resources = "/contactForm.csv", numLinesToSkip = 1)
public void testContactForm(String subjectHeading, String emailAddress, String orderReference, String message, String uploadFile) {
        $(By.id("id_contact")).selectOption(subjectHeading);
        $(By.id("email")).setValue(emailAddress);
        $(By.id("id_order")).setValue(orderReference);
        $(By.id("message")).setValue(readTextFileAsString(message));
        $(By.id("fileUpload")).uploadFile(new File(uploadFile));
        $(By.id("submitMessage")).click();
        $(By.className("alert-success")).shouldHave(Condition.text("Your message has been successfully sent to our team"));
    }

A special mention here for the UploadFile feature from Selenide that can save you from the dreadful workarounds of trying to interact with the native OS file select window.

  • Do the clean-up in the @AfterEach step:

@AfterEach
public void closeBrowser() {
        closeWebDriver();
    }

So, as seen, the main test case for this webpage can be written in a concise manner and in under 30 lines of code. Whereas going with the Gherkin approach would have implied exponentially more code and effort to structure it.