RestAssured

Now I am going to add a new Rest API test to this framework.

Let’s visit this website: https://github.com/rest-assured/rest-assured/wiki/GettingStarted

Add the maven dependency to the pom.xml as the above page suggests. Now we can add a test.

package com.directly.potassium;

import org.testng.annotations.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.CoreMatchers.*;

public class SampleApiTest extends BaseTest {
@Test
void kvstoreTest() {
String path = getPropStr("kvstore.Host") + getPropStr("kvstore.Path.Parakeet") + getPropStr("kvstore.testKey");

given().
headers("kvstoreio_api_key", getPropStr("kvstoreio_api_key")).
when().
get(path).
then().
body("value", is(getPropStr("kvstore.testExpected.value")));
}
}

We can run this test by: mvn test -Dtest=SampleApiTest

Three types of configurations

When we write integration tests, we consider three categories of configurable parameters.

  1. Environment specific
  2. Operation specific
  3. Part of the test

Many test developers learned enough to avoid hardcoding stuffs, but often they mix those in one file. Some operation specific parameters such as default timeout value can be easily overridden even if it’s mixed with other properties, but we can’t easily replace the set of environment specific parameters at the operation time. As a result, we see duplicate copies of operation specific parameters embedded in ‘test-env.properties’ and ‘stage-env.properties’. Even worse are ‘part of the test’ properties. UI tests utilizing Page Object Model will come with the xpath strings or other forms of identification for an object on a page. If these are mixed with the aforementioned property files, it would be a nightmare to maintain them.

Number of environment specific parameter files should match number of test environments in concern. Number of operation specific parameters should be ideally one. Operation specific parameters are replaced one by one but rarely by the set. Part of the test parameters should be placed in a same folder structure of the classes that need them. For example, if POM class structure has 3 depth tree structure, the resources files should match it.

In the end, we want to run a test suite like this:
>mvn test -Dgroups=regression -Denv=test-env -Dretry=3

Genesis

In the beginning, you created a folder.

>mkdir potassium

Then you made it a git repo.

>git init –bare

… wait, I don’t want to deal with github later. I’d rather create a new project on github ‘potassium’ then clone it.

>git clone git@github.com:myorg/potassium.git

>cd potassium

It feels home here. Let’s make it a maven project so we won’t need to worry about java library dependencies.

https://maven.apache.org/guides/getting-started/index.html#How_do_I_make_my_first_Maven_project

>

mvn -B archetype:generate -DgroupId=com.myorg.potassium -DartifactId=potassium -DarchetypeArtifactId=maven-archetype-potassium -DarchetypeVersion=1.4

wait .. I made a mistake of creating the maven project ‘potassium’ under the folder I created as ‘potassium’ and I’m too lazy to fix the issue. I will take those out of the folder.

>mv potassium/* .
>rm -rf potassium

I test it by running a maven command.

>mvn clean install

Nice

Add TestNG dependency and create a base test class.

Google ‘maven testng’ to get
https://mvnrepository.com/artifact/org.testng/testng

Copied block goes to pom.xml but let’s open the project in IntelliJ first to handle this.

Open IntelliJ > Imort a new project > (select potassium folder) > maven

Any dependency will go under <dependencies> block per https://maven.apache.org/pom.html

IntelliJ’s .iml file is better staying local to individual developers. I’d leave it out of git repo. Creating .gitignore file to add *.iml pattern might be useful.

>vi .gitignore
>git add .gitignore
>git add pom.xml

Now we’re ready to add a Base class and BaseTest class.

public class Base
{
    private static Properties properties; // this should exist as a singleton
    Logger logger;

    Base() {
        try {
            properties = new Properties();
            InputStream stream = getClass().getClassLoader().getResourceAsStream("default.properties");
            properties.load(stream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        logger = Logger.getLogger(getClass().getName());
    }

    Properties getProperties() {
        return properties;
    }

    void info(String str) {
        logger.log(Level.INFO, str);
    }
}
public class BaseTest extends Base
{
BaseTest() {
super();
}

@Parameters({"env"})
@BeforeSuite(alwaysRun = true)
public void beforeSuite(@Optional("test-env") String envName) {
loadTestProperties(envName + ".properties");
}

void loadTestProperties(String envFile) {
try {
info("loading properties: " + envFile);
InputStream stream = getClass().getClassLoader().getResourceAsStream(envFile);
getProperties().load(stream);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class PropertiesTest extends BaseTest {
    @Test
    public void defaultPropertiesTest()
    {
        Assert.assertEquals(getPropStr("frameworkName"), getPropStr("expectedName"));
    }
}

>mvn test