Skip to content

Commit 2abf40d

Browse files
authored
Merge pull request #822 from fabiotadashi/KTLN-626/ktor-controller-tests
Ktln 626/ktor controller tests
2 parents 7bc5dc2 + 620a16d commit 2abf40d

File tree

7 files changed

+261
-2
lines changed

7 files changed

+261
-2
lines changed

kotlin-ktor/build.gradle.kts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,18 @@ dependencies {
2121
implementation("io.ktor", "ktor-server-core", ktorVersion)
2222
implementation("io.ktor", "ktor-server-netty", ktorVersion)
2323
implementation("io.ktor", "ktor-server-websockets", ktorVersion)
24-
implementation("io.ktor","ktor-server-status-pages", ktorVersion)
24+
implementation("io.ktor", "ktor-server-status-pages", ktorVersion)
2525
implementation("io.ktor", "ktor-server-thymeleaf-jvm", ktorVersion)
2626
implementation("io.ktor", "ktor-client-auth", ktorVersion)
2727
implementation("io.ktor", "ktor-client-websockets", ktorVersion)
2828
implementation("io.ktor", "ktor-serialization-jackson", ktorVersion)
2929
implementation("io.ktor", "ktor-client-content-negotiation", ktorVersion)
3030

31-
implementation("ch.qos.logback","logback-classic", logbackVersion)
31+
implementation("ch.qos.logback", "logback-classic", logbackVersion)
3232

3333
testImplementation("io.ktor", "ktor-client-mock", ktorVersion)
3434
testImplementation("io.ktor", "ktor-server-tests", ktorVersion)
35+
3536
testImplementation("io.ktor", "ktor-serialization-kotlinx-json", ktorVersion)
3637
testImplementation("org.jetbrains.kotlin", "kotlin-test-junit", kotlinTestUnit)
3738
testImplementation("org.seleniumhq.selenium", "selenium-java", seleniumVersion)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.baeldung.testing
2+
3+
import com.baeldung.testing.plugins.configureContentNegotiation
4+
import com.baeldung.testing.plugins.configureRouting
5+
import io.ktor.server.engine.*
6+
import io.ktor.server.netty.*
7+
8+
fun main() {
9+
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
10+
configureRouting()
11+
configureContentNegotiation()
12+
}.start(wait = true)
13+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.baeldung.testing.data
2+
3+
data class Car(
4+
val id: String,
5+
var brand: String,
6+
var price: Double
7+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.baeldung.testing.data
2+
3+
object CarStorageMock {
4+
5+
val carStorage = ArrayList<Car>()
6+
7+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.baeldung.testing.plugins
2+
3+
import io.ktor.serialization.jackson.*
4+
import io.ktor.server.application.*
5+
import io.ktor.server.plugins.contentnegotiation.*
6+
7+
fun Application.configureContentNegotiation() {
8+
install(ContentNegotiation) {
9+
jackson()
10+
}
11+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.baeldung.testing.plugins
2+
3+
import com.baeldung.testing.data.Car
4+
import com.baeldung.testing.data.CarStorageMock
5+
import io.ktor.http.*
6+
import io.ktor.server.application.*
7+
import io.ktor.server.request.*
8+
import io.ktor.server.response.*
9+
import io.ktor.server.routing.*
10+
11+
fun Application.configureRouting() {
12+
routing {
13+
route("cars") {
14+
get {
15+
call.respond(CarStorageMock.carStorage)
16+
}
17+
get("{id?}") {
18+
val id = call.parameters["id"]
19+
val car = CarStorageMock.carStorage.find { it.id == id } ?: return@get call.respondText(
20+
text = "car.not.found",
21+
status = HttpStatusCode.NotFound
22+
)
23+
call.respond(car)
24+
}
25+
post {
26+
val car = call.receive<Car>()
27+
CarStorageMock.carStorage.add(car)
28+
call.respond(status = HttpStatusCode.Created, message = car)
29+
}
30+
put("{id?}") {
31+
val id = call.parameters["id"]
32+
val car = CarStorageMock.carStorage.find { it.id == id } ?: return@put call.respondText(
33+
text = "car.not.found",
34+
status = HttpStatusCode.NotFound
35+
)
36+
val carUpdate = call.receive<Car>()
37+
car.brand = carUpdate.brand
38+
car.price = carUpdate.price
39+
call.respond(car)
40+
}
41+
delete("{id?}") {
42+
val id = call.parameters["id"]
43+
if (CarStorageMock.carStorage.removeIf { it.id == id }) {
44+
call.respondText(text = "car.deleted", status = HttpStatusCode.OK)
45+
} else {
46+
call.respondText(text = "car.not.found", status = HttpStatusCode.NotFound)
47+
}
48+
}
49+
}
50+
}
51+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package com.baeldung.testing
2+
3+
import com.baeldung.testing.data.Car
4+
import com.baeldung.testing.data.CarStorageMock
5+
import com.baeldung.testing.plugins.configureContentNegotiation
6+
import com.baeldung.testing.plugins.configureRouting
7+
import io.ktor.client.*
8+
import io.ktor.client.call.*
9+
import io.ktor.client.plugins.contentnegotiation.*
10+
import io.ktor.client.request.*
11+
import io.ktor.client.statement.*
12+
import io.ktor.http.*
13+
import io.ktor.serialization.jackson.*
14+
import io.ktor.server.testing.*
15+
import org.junit.Before
16+
import kotlin.test.Test
17+
import kotlin.test.assertEquals
18+
19+
class CarRouteTests {
20+
21+
@Before
22+
fun before() {
23+
CarStorageMock.carStorage.clear()
24+
}
25+
26+
@Test
27+
fun `when get cars then should return a list with cars`() = testApplication {
28+
val client = configureServerAndGetClient()
29+
CarStorageMock.carStorage.addAll(
30+
listOf(
31+
Car(id = "1", brand = "BMW", price = 10_000.0),
32+
Car(id = "2", brand = "Audi", price = 11_000.0)
33+
)
34+
)
35+
36+
val response = client.get("/cars")
37+
val responseBody: List<Car> = response.body()
38+
39+
assertEquals(HttpStatusCode.OK, response.status)
40+
assertEquals(2, responseBody.size)
41+
42+
val bmwCar = responseBody.find { it.id == "1" }
43+
assertEquals("BMW", bmwCar?.brand)
44+
assertEquals(10_000.0, bmwCar?.price)
45+
46+
val audiCar = responseBody.find { it.id == "2" }
47+
assertEquals("Audi", audiCar?.brand)
48+
assertEquals(11_000.0, audiCar?.price)
49+
}
50+
51+
@Test
52+
fun `when get specific car id then should return a that car`() = testApplication {
53+
val client = configureServerAndGetClient()
54+
CarStorageMock.carStorage.add(Car(id = "1", brand = "BMW", price = 10_000.0))
55+
56+
val response = client.get("/cars/1")
57+
val responseBody: Car = response.body()
58+
59+
assertEquals(HttpStatusCode.OK, response.status)
60+
assertEquals("1", responseBody.id)
61+
assertEquals("BMW", responseBody.brand)
62+
}
63+
64+
@Test
65+
fun `when get specific car id with wrong id then should return error`() = testApplication {
66+
val client = configureServerAndGetClient()
67+
CarStorageMock.carStorage.add(Car(id = "1", brand = "BMW", price = 10_000.0))
68+
69+
val response = client.get("/cars/3")
70+
val responseText = response.bodyAsText()
71+
72+
assertEquals(HttpStatusCode.NotFound, response.status)
73+
assertEquals("car.not.found", responseText)
74+
}
75+
76+
@Test
77+
fun `when post cars then should create a car and return it`() = testApplication {
78+
val client = configureServerAndGetClient()
79+
80+
val response = client.post("/cars") {
81+
contentType(ContentType.Application.Json)
82+
setBody(Car(id = "2", brand = "Audi", price = 11_000.0))
83+
}
84+
val responseBody: Car = response.body()
85+
86+
assertEquals(HttpStatusCode.Created, response.status)
87+
assertEquals("2", responseBody.id)
88+
assertEquals("Audi", responseBody.brand)
89+
assertEquals(1, CarStorageMock.carStorage.size)
90+
}
91+
92+
@Test
93+
fun `when put cars then should update a car and return it`() = testApplication {
94+
val client = configureServerAndGetClient()
95+
96+
CarStorageMock.carStorage.add(Car(id = "1", brand = "BMW", price = 10_000.0))
97+
98+
val response = client.put("/cars/1") {
99+
contentType(ContentType.Application.Json)
100+
setBody(Car(id = "1", brand = "Audi", price = 11_000.0))
101+
}
102+
val responseBody: Car = response.body()
103+
104+
assertEquals(HttpStatusCode.OK, response.status)
105+
assertEquals("1", responseBody.id)
106+
assertEquals("Audi", responseBody.brand)
107+
assertEquals("Audi", CarStorageMock.carStorage.find { it.id == "1" }?.brand)
108+
}
109+
110+
@Test
111+
fun `when put cars with wrong id then should return error`() = testApplication {
112+
val client = configureServerAndGetClient()
113+
114+
CarStorageMock.carStorage.add(Car(id = "1", brand = "BMW", price = 10_000.0))
115+
116+
val response = client.put("/cars/2") {
117+
contentType(ContentType.Application.Json)
118+
setBody(Car(id = "1", brand = "Audi", price = 11_000.0))
119+
}
120+
121+
val responseText = response.bodyAsText()
122+
123+
assertEquals(HttpStatusCode.NotFound, response.status)
124+
assertEquals("car.not.found", responseText)
125+
}
126+
127+
@Test
128+
fun `when delete a car then should return ok`() = testApplication {
129+
val client = configureServerAndGetClient()
130+
131+
CarStorageMock.carStorage.add(Car(id = "1", brand = "BMW", price = 10_000.0))
132+
133+
val response = client.delete("/cars/1")
134+
135+
val responseText = response.bodyAsText()
136+
137+
assertEquals(HttpStatusCode.OK, response.status)
138+
assertEquals("car.deleted", responseText)
139+
}
140+
141+
@Test
142+
fun `when delete a car with wrong id then should return error`() = testApplication {
143+
val client = configureServerAndGetClient()
144+
145+
CarStorageMock.carStorage.add(Car(id = "1", brand = "BMW", price = 10_000.0))
146+
147+
val response = client.delete("/cars/2")
148+
149+
val responseText = response.bodyAsText()
150+
151+
assertEquals(HttpStatusCode.NotFound, response.status)
152+
assertEquals("car.not.found", responseText)
153+
154+
}
155+
156+
private fun ApplicationTestBuilder.configureServerAndGetClient(): HttpClient {
157+
application {
158+
configureRouting()
159+
configureContentNegotiation()
160+
}
161+
val client = createClient {
162+
install(ContentNegotiation) {
163+
jackson()
164+
}
165+
}
166+
return client
167+
}
168+
169+
}

0 commit comments

Comments
 (0)