What we're going to test and how we're going to do it?
I've chosen an open API https://fakestoreapi.com/
It has GET, POST, PUT, PATCH and DELETE methods so we'll be able to try writing tests not only for GET methods.
I'd like to write smoke tests first as a best practice. It'll be some positive cases like getting an info about exact product, list of all the products, addition of a new product and deleting it.
When working with APIs, creating CRUD (Create, Read, Update, Delete) tests is essential to validate the functionality of your endpoints.
Let's start from smth small!
🚲Step by step
We will use the RestAssured
library for HTTP interactions and JUnit
for test assertions.
Project Setup
- Add these dependencies to your
build.gradle
:
dependencies {
testImplementation "io.rest-assured:rest-assured:4.3.3"
testImplementation "org.junit.jupiter:junit-jupiter:5.7.0"
testImplementation "com.google.code.gson:gson:2.8.6"
}
- Import necessary packages in your test file:
import io.restassured.RestAssured.*
import io.restassured.http.ContentType
import io.restassured.response.Response
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import com.google.gson.Gson
We'll test the /products
resource in FakeStoreAPI.
1. GET Request (Positive and Negative)
A GET request fetches data from the API. We’ll test fetching a single product and handling an invalid ID.
Positive Scenario
@Test
fun `test get product by ID - positive`() {
val response: Response = given()
.contentType(ContentType.JSON)
.get("https://fakestoreapi.com/products/1")
.then()
.statusCode(200)
.extract().response()
val jsonResponse = response.asString()
assertNotNull(jsonResponse)
assertTrue(jsonResponse.contains("id"))
println("Response: $jsonResponse")
}
Negative Scenario (Invalid Product ID)
@Test
fun `test get product by invalid ID - negative`() {
val response: Response = given()
.contentType(ContentType.JSON)
.get("https://fakestoreapi.com/products/999999")
.then()
.statusCode(404) // Expecting Not Found
.extract().response()
val jsonResponse = response.asString()
assertTrue(jsonResponse.contains("error"))
println("Response: $jsonResponse")
}
2. POST Request (Create Product)
In a POST request, we send data to create a new resource. We'll create a new product and handle the response.
Positive Scenario
data class Product(val title: String, val price: Double, val description: String, val category: String, val image: String)
@Test
fun `test create product - positive`() {
val product = Product("New Product", 29.99, "A test product", "electronics", "https://i.imgur.com/EHyR2nP.png")
val response: Response = given()
.contentType(ContentType.JSON)
.body(Gson().toJson(product))
.post("https://fakestoreapi.com/products")
.then()
.statusCode(201)
.extract().response()
val jsonResponse = response.asString()
assertTrue(jsonResponse.contains("New Product"))
println("Response: $jsonResponse")
}
Negative Scenario (Missing Fields)
@Test
fun `test create product with missing fields - negative`() {
val incompleteProduct = mapOf("title" to "Incomplete Product")
val response: Response = given()
.contentType(ContentType.JSON)
.body(Gson().toJson(incompleteProduct))
.post("https://fakestoreapi.com/products")
.then()
.statusCode(400) // Bad Request due to missing required fields
.extract().response()
val jsonResponse = response.asString()
assertTrue(jsonResponse.contains("error"))
println("Response: $jsonResponse")
}
3. PUT Request (Update Product)
A PUT request is used to update an existing resource. We’ll update the product’s price.
Positive Scenario
@Test
fun `test update product - positive`() {
val updatedProduct = Product("Updated Product", 49.99, "Updated description", "electronics", "https://i.imgur.com/EHyR2nP.png")
val response: Response = given()
.contentType(ContentType.JSON)
.body(Gson().toJson(updatedProduct))
.put("https://fakestoreapi.com/products/1")
.then()
.statusCode(200)
.extract().response()
val jsonResponse = response.asString()
assertTrue(jsonResponse.contains("Updated Product"))
assertTrue(jsonResponse.contains("49.99"))
println("Response: $jsonResponse")
}
Negative Scenario (Invalid Product ID)
@Test
fun `test update product with invalid ID - negative`() {
val updatedProduct = Product("Non-existent Product", 19.99, "This should fail", "electronics", "https://i.imgur.com/EHyR2nP.png")
val response: Response = given()
.contentType(ContentType.JSON)
.body(Gson().toJson(updatedProduct))
.put("https://fakestoreapi.com/products/999999")
.then()
.statusCode(404)
.extract().response()
val jsonResponse = response.asString()
assertTrue(jsonResponse.contains("error"))
println("Response: $jsonResponse")
}
4. PATCH Request (Partial Update)
A PATCH request is used to partially update a resource. Let’s update just the price of an existing product.
Positive Scenario
@Test
fun `test patch product price - positive`() {
val updateData = mapOf("price" to 59.99)
val response: Response = given()
.contentType(ContentType.JSON)
.body(Gson().toJson(updateData))
.patch("https://fakestoreapi.com/products/1")
.then()
.statusCode(200)
.extract().response()
val jsonResponse = response.asString()
assertTrue(jsonResponse.contains("59.99"))
println("Response: $jsonResponse")
}
Negative Scenario (Invalid Field)
@Test
fun `test patch product with invalid field - negative`() {
val invalidUpdateData = mapOf("nonExistentField" to "new value")
val response: Response = given()
.contentType(ContentType.JSON)
.body(Gson().toJson(invalidUpdateData))
.patch("https://fakestoreapi.com/products/1")
.then()
.statusCode(400)
.extract().response()
val jsonResponse = response.asString()
assertTrue(jsonResponse.contains("error"))
println("Response: $jsonResponse")
}
5. DELETE Request (Delete a Product)
The DELETE request removes a resource from the server.
Positive Scenario
@Test
fun `test delete product - positive`() {
val response: Response = given()
.contentType(ContentType.JSON)
.delete("https://fakestoreapi.com/products/1")
.then()
.statusCode(200)
.extract().response()
val jsonResponse = response.asString()
assertTrue(jsonResponse.contains("1"))
println("Response: $jsonResponse")
}
Negative Scenario (Deleting Non-existent Product)
@Test
fun `test delete non-existent product - negative`() {
val response: Response = given()
.contentType(ContentType.JSON)
.delete("https://fakestoreapi.com/products/999999")
.then()
.statusCode(404)
.extract().response()
val jsonResponse = response.asString()
assertTrue(jsonResponse.contains("error"))
println("Response: $jsonResponse")
}
Conclusion
By using Kotlin, RestAssured
, and JUnit, you can automate the process of testing API endpoints for various HTTP requests like GET, POST, PUT, PATCH, and DELETE. Testing both positive and negative scenarios helps ensure your API handles real-world cases and edge cases effectively.
Now, you can use these examples to build your own test cases and start automating the validation of your RESTful APIs!
Top comments (0)