Tartalomjegyzék

Rest Assured alapjai (REST API tesztelés)

REST Assured Beginner Tutorial

REST API-s projektjeim magyarázatokkal:

Az api tesztekkel a szerződés ellenőrzését végezzük el.

Projekt létrehozása

Fájl -> new project -> maven -> create a simple project -> next group id: hu.tanulas artifact id: proba -> Finish

Jobb klikk a projekt mappán -> Build path -> Configure build path -> JRE System Library 21 legyen Utána bal oldalt kattints a Java Compiler-re és java 21-et állítsd be. Apply and close

A pom.xml-be az első sor végén lévő http-t írd át https-re!

És ezt másold be:

	  <dependencies>
	 
		  <!-- Source: https://mvnrepository.com/artifact/io.rest-assured/rest-assured -->
		<dependency>
		    <groupId>io.rest-assured</groupId>
		    <artifactId>rest-assured</artifactId>
		    <version>5.5.6</version>
		    <scope>test</scope>
		</dependency>
		
	  </dependencies>

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>hu.tanulas</groupId>
  <artifactId>proba</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  
	  <dependencies>
	  
		  <!-- Source: https://mvnrepository.com/artifact/io.rest-assured/rest-assured -->
		<dependency>
		    <groupId>io.rest-assured</groupId>
		    <artifactId>rest-assured</artifactId>
		    <version>5.5.6</version>
		    <scope>test</scope>
		</dependency>
		
		<!--Maven Wrapper, hogy Eclipse nélkül is lehessen futtatni. -->
		<!-- Source: https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
		<dependency>
		    <groupId>org.junit.jupiter</groupId>
		    <artifactId>junit-jupiter</artifactId>
		    <version>5.11.0</version>
		    <scope>test</scope>
		</dependency>
		
		<!-- Source: https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
		<dependency>
		    <groupId>org.apache.logging.log4j</groupId>
		    <artifactId>log4j-core</artifactId>
		    <version>2.26.0</version>
		    <scope>compile</scope>
		</dependency>
	
		<!--A response válaszokból esetleges objektumképzéshez szükség van json feldolgozó importra is, pl.: jackson -->
		<!-- Source: https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
		<dependency>
		    <groupId>com.fasterxml.jackson.core</groupId>
		    <artifactId>jackson-databind</artifactId>
		    <version>2.17.1</version>
		    <scope>compile</scope>
		</dependency>

	  </dependencies>
	  
	  <!--Verzió megadása, de nem kötelező ezeknél is működik. Ezzel még biztosabb lesz a program.-->
	  <properties>
	  	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	  	<maven.compiler.release>21</maven.compiler.release>
	  	<maven.compiler.target>21</maven.compiler.target>
	  </properties>
	  
	  
	  <build>
	    <plugins>
	        <plugin>
	            <groupId>org.apache.maven.plugins</groupId>
	            <artifactId>maven-surefire-plugin</artifactId>
	            <version>3.5.4</version>	         
	        </plugin>
	        
	        <plugin>
	            <groupId>org.apache.maven.plugins</groupId>
	            <artifactId>maven-compiler-plugin</artifactId>
	            <version>3.12.1</version>	
	            <configuration>      
	            	<release>21</release>
	            </configuration>     
	        </plugin>
	        <!--Utána navigálj el a cmd-ben a projekt mappáig, ahol a pom.xml van.
	        Aztán mvn clean test
	        -->
	    </plugins>
	</build>

	  
</project>

BaseApiTest osztály

A base package-be rakd és innen származtasd a többit teszt osztályt.

package api.base;

import org.junit.jupiter.api.BeforeEach;

import io.restassured.RestAssured;
import io.restassured.http.ContentType;

public class BaseApiTest {

	@BeforeEach
	void setup() {
        // Tesztelni kívánt weboldal címe.
		RestAssured.baseURI="https://jsonplaceholder.typicode.com/";
		
		RestAssured.requestSpecification=RestAssured
				.given()	
					.accept(ContentType.JSON); // json formátumú választ várunk 
	}
}

CI bekapcsolása

.github/workflows/maven-tests.yml

name: Run Rest Assured API Tests

on:
  push:
  pull_request:
  workflow_dispatch:

jobs:
  tests:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        
      - name: Set up JDK21
        uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '21'
          
      - name: Run Maven Tests
        run: mvn -B clean test -DCI=true
    
      - name: Upload surefire test reports
        uses: actions/upload-artifact@v4
        with:
          name: surefire-reports
          path: target/surefire-reports

Gitignore

# =========================
# Java compiled files
# =========================
*.class

# =========================
# Maven
# =========================
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml

# =========================
# IntelliJ IDEA
# =========================
.idea/
*.iml
out/

# =========================
# Eclipse
# =========================
.project
.classpath
.settings/
.metadata/
bin/

# =========================
# VS Code
# =========================
.vscode/

# =========================
# OS files
# =========================
.DS_Store
Thumbs.db

# =========================
# Logs
# =========================
*.log

# =========================
# Selenium / test outputs
# =========================
screenshots/
reports/
allure-results/
allure-report/

# =========================
# Sonar
# =========================
.scannerwork/
.sonar/

# =========================
# Coverage
# =========================
coverage/
jacoco.exec

# =========================
# Temporary files
# =========================
*.tmp
*.temp
*.swp

# =========================
# Secrets and env, config.properties
# =========================
.env
.secrets
config.properties

Maven wrapper beállítása és használata

A terminálban navigálj el a projekt mappájáig (pl.: cd GitHub/saucedemo) mvn -v Ezzel lecsekkoljuk, hogy van-e a gépen maven, ha nem ír verziót, a Mavent telepíteni kell: https://maven.apache.org/download.cgi

pl. apache-maven-3.9.16-bin.zip letöltés, kicsomagolás környezeti változókhoz felvenni a bin mappáját

Linux-on még ez is kell egyszer: sudo apt install maven

Utána ezt futtasd: mvn clean test

Ezután jöhet a csomagolás: mvn wrapper:wrapper Ezután létrejönnek ezek: mvnw és mvnw.cmd fájlok és .mvn mappa. Innentől kezdve nem mvn utasítást kell használni, hanem mvnw utasítást! Ezután nem gond, ah nincsen maven a gépen telepítve, simán lehet terminálból is futatni a projektet.

Futtatás linux-on: ./mvnw clean test

win-on: mvnw clean test

Teszt osztályok létrehozása

Minden package-t az src/test/java-ban hozz létre. Az api package-n belül pl.: api.UserList -> JUnit Test Case, csekkold, hogy tuti a Jupiter van felül kijelölve.

REST API tesztek röviden

A body vizsgálatoknál két paraméter határozza meg az elvárt eredmény ellenőrzését:

  1. paraméter - meghatározza az adatot, amire az ellenőrzés vonatkozik Ez egy json Path https://github.com/rest-assured/rest-assured/wiki/Usage#complex-json-paths

  2. paraméter - Hamcrest matcher, ezen keresztül mondjuk meg, hogy mit várunk el az adattal kapcsolatban. Rengeteg matchers létezik, pl.: nullValue, notNullValue, greaterThan, lessThan, equalTo, hasItem, anEmptyMap stb.: https://hamcrest.org/JavaHamcrest/javadoc/2.2/org/hamcrest/Matchers.html

Lentebb lesz külön részletezve.

Loggolások

Surfire report: A teszt eredményét foglalja össze. Futás utáni összegzés.

Log: A hibakeresést segíti. [.then után -> .log().ifValidationFails() // A jó log, csak baj esetén szól (ha pl.: létező id-t adunk meg)]

Futás közbeni összegzés.

Loggolás használata

package api.usersUpdate;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;

import org.apache.logging.log4j.LogManager;
import  org.apache.logging.log4j.Logger;

import org.junit.jupiter.api.Test;

import api.base.BaseApiTest;
import io.restassured.http.ContentType;

class UserUpdateTest extends BaseApiTest {

	//import org.apache.logging.log4j.Logger; 
	//A végén lévő zárójelben magát az osztályt kell megadni. 
	private static final Logger LOG=LogManager.getLogger(UserUpdateTest.class);
			
	@Test
	void userUpdateTestWithLog() {
		LOG.info("TC4.1 indítása, felhasználó módosítása");
		//Szövegblokk. Bemeneti adatok.
		String requestBody= """
				{	
					"name":"Teszt Elek",						
					"email":"tesztelek@teszt.com"
				}
				""";
		int userID=1;
		
		LOG.info("Módosítandó id: {} ",userID);
		
		//Kijelölöd a lento blokkot. Jobb klikk -> Surround with -> Try-catch Block
		try {
			//A restassured fluens api megoldást használ. 
			given()
				.contentType(ContentType.JSON)
				.body(requestBody)
			.when()	
				.put("/users/"+userID)
			.then()
				.log().ifValidationFails()
				.statusCode(200)
				.body("id", equalTo(1))
				.body("name", equalTo("Teszt Elek"))
				.body("email", equalTo("tesztelek@teszt.com"));
			
			LOG.info("TC4.1 sikeresen lefutott");
		} catch (Exception e) {
			LOG.error("Hiba történt a teszt futtatása közben: ",e);
			throw e;
		}
	}
}

Az src/test/recources/log4j2.xml fájlba:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"> <!-- Log4g-nek a belső működésére vonatkozik.  -->

    <Appenders>
    <!-- A konzolja írja ki a szöveget. -->
        <Console name="Console" target="SYSTEM_OUT">
        <!-- Megjelenítési forma.  -->
            <PatternLayout
                pattern="%d{HH:mm:ss} %-5level %logger - %msg%n"/>
        </Console>
    </Appenders>

    <Loggers>
        <Root level="INFO"> <!-- Infó szintől felfelé kerül megjelenítésre a loggolás. -->
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>

</Configuration>

POJO

Külön package-et hozunk létre a POJO osztályok számára, amelyekben az objektumokat tároljuk.

A POJO (Plain Old Java Object) egyszerű Java osztály, amely általában csak adatokat tartalmaz, például mezőket, gettereket és settereket. Az elkülönített package segít a kód áttekinthetőségében és rendszerezésében.

Így néz ki egy osztálya:

package userPOJO;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

//Csak azokat a mezőket veszi figyelembe, 
//amiket az osztály adattagjaiként létrehozunk.
@JsonIgnoreProperties(ignoreUnknown=true)
public class User {
	
	private int id;
	private String name;
	private String username;
	private String email;
	
	public int getId() {
		return id;
	}
	public String getName() {
		return name;
	}
	public String getUsername() {
		return username;
	}
	public String getEmail() {
		return email;
	}
	
}

A REST Assured kérés felépítése

Importja: import static io.restassured.RestAssured.*;

given()
    .header(...)
    .contentType(ContentType.JSON)
    .body(requestBody)

.when()
    .post("/users")

.then()
    .statusCode(201)
    .body("name", equalTo("John"));

given()

A kérés előkészítése.

MetódusMire való?Példa
header()HTTP fejléc hozzáadása.header("Authorization", "Bearer token")
headers()Több fejléc egyszerre.headers("Accept", "application/json")
contentType()Request Content-Type.contentType(ContentType.JSON)
accept()Elfogadott válasz típusa.accept(ContentType.JSON)
body()Request Body.body(requestBody)
queryParam()Query paraméter.queryParam("page", 2)
pathParam()Path paraméter.pathParam("id", 5)
formParam()Form paraméter.formParam("username", "admin")
cookie()Cookie küldése.cookie("sessionId", "abc123")
auth()Hitelesítés.auth().basic("user", "pass")
log().all()Teljes kérés naplózása.log().all()
log().body()Csak a body naplózása.log().body()
log().headers()Csak a headerek naplózása.log().headers()
log().ifValidationFails()Csak sikertelen validáció esetén naplóz.log().ifValidationFails()

when()

A HTTP kérés elküldése.

MetódusMire való?Példa
get()GET kérés.get("/users")
post()POST kérés.post("/users")
put()PUT kérés.put("/users/2")
patch()PATCH kérés.patch("/users/2")
delete()DELETE kérés.delete("/users/2")
options()OPTIONS kérés.options("/users")
head()HEAD kérés.head("/users")

then()

A válasz ellenőrzése.

MetódusMire való?Példa
statusCode()HTTP státuszkód ellenőrzése.statusCode(200)
statusLine()HTTP státusz sor.statusLine("HTTP/1.1 200 OK")
contentType()Content-Type ellenőrzése.contentType(ContentType.JSON)
body()JSON mező ellenőrzése.body("name", equalTo("John"))
header()Header ellenőrzése.header("Server", "cloudflare")
headers()Több header ellenőrzése.headers(...)
cookie()Cookie ellenőrzése.cookie("sessionId")
time()Válaszidő ellenőrzése.time(lessThan(3000L))
log().all()Teljes válasz naplózása.log().all()
log().body()Csak a body naplózása.log().body()
log().ifValidationFails()Csak sikertelen validáció esetén naplóz.log().ifValidationFails()

extract()

Az ellenőrzés után adatokat nyerhetsz ki.

MetódusMire való?Példa
extract().response()Teljes Response objektum.extract().response()
extract().path()Egy mező kiolvasása.extract().path("data.id")
extract().jsonPath()JsonPath objektum.extract().jsonPath()
extract().xmlPath()XML válasz feldolgozása.extract().xmlPath()
extract().as()POJO-vá alakítás.extract().as(User.class)

Response

Importja: import io.restassured.response.Response;

MetódusMire való?Példa
getStatusCode()HTTP státuszkódresponse.getStatusCode()
getStatusLine()HTTP státusz sorresponse.getStatusLine()
getBody().asString()Body Stringkéntresponse.getBody().asString()
jsonPath().getObject()Objektum kiolvasásaresponse.jsonPath().getObject("data",User.class)
jsonPath().getString()String kiolvasásaresponse.jsonPath().getString("name")
jsonPath().getInt()Integer kiolvasásaresponse.jsonPath().getInt("id")
jsonPath().getList()Lista kiolvasásaresponse.jsonPath().getList("data")
prettyPrint()Formázott JSON kiírásaresponse.prettyPrint()
time()Válaszidőresponse.time()

A response.jsonPath().getObject("data",User.class)-nél a "data" a mezőköz. A User.class a POJO osztályl jön. Viszont a putnál a mezőköz legyne üres "";

Ha nincs mezőköz, akkor .extract().as(User.class)-t kell használni.

Gyakori HTTP státuszkódok

KódJelentés
200OK
201Created
202Accepted
204No Content
400Bad Request
401Unauthorized
403Forbidden
404Not Found
405Method Not Allowed
409Conflict
415Unsupported Media Type
422Unprocessable Entity
500Internal Server Error

Legfontosabb importok

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.*;

import io.restassured.http.ContentType;
import io.restassured.response.Response;

Hamcrest Matcherek Rest Assured-ben

MatcherMire való?Példa
equalTo()Pontosan megegyezik.body("name", equalTo("Janet"))
not()Negálás.body("name", not(equalTo("Peter")))
is()Olvashatóbb szintaxis.body("id", is(2))
nullValue()Az érték null.body("data", nullValue())
notNullValue()Az érték nem null.body("id", notNullValue())
empty()Üres lista vagy Map.body("data", empty())
emptyString()Üres String.body("name", emptyString())
hasSize()Lista mérete.body("data", hasSize(6))
hasItem()Tartalmaz egy elemet.body("data.email", hasItem("janet.weaver@reqres.in"))
hasItems()Több elemet is tartalmaz.body("data.id", hasItems(1,2,3))
contains()Pontosan ezek az elemek ebben a sorrendben.body("data.id", contains(1,2,3))
containsInAnyOrder()Pontosan ezek az elemek, sorrend mindegy.body("data.id", containsInAnyOrder(3,1,2))
everyItem()Minden elem megfelel.body("data.email", everyItem(containsString("@")))
hasKey()Map tartalmaz kulcsot.body("$", hasKey("data"))
hasValue()Map tartalmaz értéket.body("$", hasValue(2))
containsString()Tartalmaz egy szövegrészt.body("email", containsString("@"))
startsWith()Szöveg ezzel kezdődik.body("email", startsWith("janet"))
endsWith()Szöveg ezzel végződik.body("email", endsWith(".in"))
matchesPattern()Regex alapján egyezik.body("email", matchesPattern(".*@reqres\\.in"))
greaterThan()Nagyobb mint.body("id", greaterThan(0))
greaterThanOrEqualTo()Nagyobb vagy egyenlő.body("page", greaterThanOrEqualTo(1))
lessThan()Kisebb mint.body("id", lessThan(100))
lessThanOrEqualTo()Kisebb vagy egyenlő.body("page", lessThanOrEqualTo(10))
closeTo()Közel van egy számhoz.body("price", closeTo(10.0, 0.5))
instanceOf()Adott típusú objektum.body("id", instanceOf(Integer.class))
sameInstance()Ugyanaz az objektumRitkán használják Rest Assured-ben
anyOf()Legalább egy feltétel igaz.body("status", anyOf(is("active"), is("inactive")))
allOf()Minden feltétel igaz.body("id", allOf(greaterThan(0), lessThan(100)))

Leggyakrabban használtak

EllenőrzésMatcher
EgyenlőségequalTo()
Nem egyenlőnot(equalTo())
Nem nullnotNullValue()
NullnullValue()
Nem üresnot(empty())
Lista méretehasSize()
Lista tartalmaz elemethasItem()
Lista több elemet tartalmazhasItems()
Minden elem megfeleleveryItem()
Szövegrész keresésecontainsString()
Ezzel kezdődikstartsWith()
Ezzel végződikendsWith()
Regex ellenőrzésmatchesPattern()
Nagyobb mintgreaterThan()
Kisebb mintlessThan()
Több feltétel egyszerreallOf()
Több lehetőség közül egyikanyOf()

Mezők létezését, így kell ellenőrizni: everyItem(allOf(hasKey("id"),hasKey("password")))

Nem üresek-e az id mezők: body("data.id", everyItem(notNullValue()));

String esetén: body("data.password", everyItem(not(isEmptyString())));

Nem üres (teljes body válasz, lista, tömb, map vagy string): not(empty())

Null érték biztosabb ellenőrzése: .body("$", anyOf(anEmptyMap(), nullValue(), empty()))

A lista legalább egy elemet tartalmaz: body("$.size()", greaterThan(0))