From 9b6f3aab88c2305ee6ae634efd17df1283ca8435 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Fri, 23 Aug 2019 13:04:20 +0200 Subject: [PATCH] Refactoring of cucumber implementation --- gradle.properties | 2 +- src/main/kotlin/fr/dcproject/Application.kt | 2 +- src/main/kotlin/fr/dcproject/Configuration.kt | 3 +- src/test/kotlin/RunCucumberTest.kt | 23 ++-- src/test/kotlin/feature/Context.kt | 44 ------- .../kotlin/feature/KtorServerAuthSteps.kt | 56 +++++++++ src/test/kotlin/feature/KtorServerContext.kt | 54 ++++++++ .../kotlin/feature/KtorServerRequestSteps.kt | 64 ++++++++++ .../kotlin/feature/KtorServerRestSteps.kt | 55 +++++++++ src/test/kotlin/feature/Request.kt | 116 ------------------ src/test/resources/feature/articles.feature | 7 +- src/test/resources/feature/citizen.feature | 4 +- .../resources/feature/constitution.feature | 6 +- src/test/resources/feature/follow.feature | 12 +- 14 files changed, 259 insertions(+), 189 deletions(-) delete mode 100644 src/test/kotlin/feature/Context.kt create mode 100644 src/test/kotlin/feature/KtorServerAuthSteps.kt create mode 100644 src/test/kotlin/feature/KtorServerContext.kt create mode 100644 src/test/kotlin/feature/KtorServerRequestSteps.kt create mode 100644 src/test/kotlin/feature/KtorServerRestSteps.kt delete mode 100644 src/test/kotlin/feature/Request.kt diff --git a/gradle.properties b/gradle.properties index 448427d..78095a5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ logback_version=1.2.1 postgresjson_version=0.1 koinVersion=2.0.1 jackson_version=2.9.9 -cucumber_version=4.3.1 +cucumber_version=4.7.1 diff --git a/src/main/kotlin/fr/dcproject/Application.kt b/src/main/kotlin/fr/dcproject/Application.kt index 543b1df..4ea3400 100644 --- a/src/main/kotlin/fr/dcproject/Application.kt +++ b/src/main/kotlin/fr/dcproject/Application.kt @@ -17,7 +17,6 @@ import fr.postgresjson.migration.Migrations import io.ktor.application.Application import io.ktor.application.install import io.ktor.auth.Authentication -import io.ktor.auth.authenticate import io.ktor.auth.jwt.jwt import io.ktor.features.AutoHeadResponse import io.ktor.features.CallLogging @@ -140,6 +139,7 @@ fun Application.module() { } install(Routing) { +// trace { application.log.trace(it.buildText()) } authenticate(optional = true) { article(get()) auth(get()) diff --git a/src/main/kotlin/fr/dcproject/Configuration.kt b/src/main/kotlin/fr/dcproject/Configuration.kt index 388ac03..ff9e6fb 100644 --- a/src/main/kotlin/fr/dcproject/Configuration.kt +++ b/src/main/kotlin/fr/dcproject/Configuration.kt @@ -21,8 +21,7 @@ class Config { } object JwtConfig { - - private const val secret = "zAP5MBA4B4Ijz0MZaS48" + const val secret = "zAP5MBA4B4Ijz0MZaS48" private const val issuer = "dc-project.fr" private const val validityInMs = 36_000_00 * 10 // 10 hours // TODO change to RSA512 diff --git a/src/test/kotlin/RunCucumberTest.kt b/src/test/kotlin/RunCucumberTest.kt index 8568bf8..736aca1 100644 --- a/src/test/kotlin/RunCucumberTest.kt +++ b/src/test/kotlin/RunCucumberTest.kt @@ -1,25 +1,21 @@ -import cucumber.api.CucumberOptions -import cucumber.api.Scenario -import cucumber.api.java8.En -import cucumber.api.junit.Cucumber -import feature.Context +import feature.KtorServerContext import fr.dcproject.config import fr.dcproject.module import fr.dcproject.utils.LoggerDelegate import fr.postgresjson.connexion.Connection import fr.postgresjson.connexion.Requester import fr.postgresjson.migration.Migrations +import io.cucumber.core.api.Scenario +import io.cucumber.java8.En +import io.cucumber.junit.Cucumber +import io.cucumber.junit.CucumberOptions import io.ktor.locations.KtorExperimentalLocationsAPI -import io.ktor.server.testing.TestApplicationEngine -import io.ktor.server.testing.createTestEnvironment import io.ktor.server.testing.withTestApplication import io.ktor.util.KtorExperimentalAPI import org.junit.runner.RunWith import org.koin.test.KoinTest import org.koin.test.get import org.slf4j.Logger -import java.util.concurrent.TimeUnit -import feature.Context.Companion.current as contextCurrent var unitialized: Boolean = false @@ -29,6 +25,11 @@ var unitialized: Boolean = false @CucumberOptions(plugin = ["pretty"]) class RunCucumberTest: En, KoinTest { private val logger: Logger? by LoggerDelegate() + + val ktorContext = KtorServerContext { + module() + } + init { Before(-2) { _: Scenario -> if (!unitialized) { @@ -48,11 +49,11 @@ class RunCucumberTest: En, KoinTest { config.database = "test" config.username = "test" config.password = "test" - contextCurrent = Context(TestApplicationEngine(createTestEnvironment()) {}, scenario) + ktorContext.start() } After { _: Scenario -> - contextCurrent.engine.stop(0L, 0L, TimeUnit.MILLISECONDS) + ktorContext.stop() } } diff --git a/src/test/kotlin/feature/Context.kt b/src/test/kotlin/feature/Context.kt deleted file mode 100644 index 5960795..0000000 --- a/src/test/kotlin/feature/Context.kt +++ /dev/null @@ -1,44 +0,0 @@ -package feature - -import cucumber.api.Scenario -import fr.dcproject.module -import io.ktor.application.Application -import io.ktor.locations.KtorExperimentalLocationsAPI -import io.ktor.server.testing.TestApplicationCall -import io.ktor.server.testing.TestApplicationEngine -import io.ktor.server.testing.TestApplicationRequest -import io.ktor.util.KtorExperimentalAPI - -@KtorExperimentalLocationsAPI -@KtorExperimentalAPI -class Context( - val engine: TestApplicationEngine, - val scenario: Scenario -) { - companion object { - lateinit var current: Context - } - - init { - engine.start() - val moduleFunction: Application.() -> Unit = { module() } - val test: TestApplicationEngine.() -> Unit = { - moduleFunction(application) - } - engine.test() - } - - var call: TestApplicationCall? = null - - private val requestContextConfigurations: MutableList Unit> = mutableListOf() - fun setupRequest(testApplicationRequest: TestApplicationRequest) { - requestContextConfigurations.forEach { - it(testApplicationRequest) - } - } - fun setupNextRequests(requestContextConfiguration: TestApplicationRequest.() -> Unit) = requestContextConfigurations.add(requestContextConfiguration) -} - -fun TestApplicationRequest.applyConfigurations() { - Context.current.setupRequest(this) -} \ No newline at end of file diff --git a/src/test/kotlin/feature/KtorServerAuthSteps.kt b/src/test/kotlin/feature/KtorServerAuthSteps.kt new file mode 100644 index 0000000..04a5c76 --- /dev/null +++ b/src/test/kotlin/feature/KtorServerAuthSteps.kt @@ -0,0 +1,56 @@ +package feature + +import com.auth0.jwt.JWT +import com.auth0.jwt.algorithms.Algorithm +import fr.dcproject.JwtConfig +import fr.dcproject.entity.Citizen +import fr.dcproject.entity.User +import fr.postgresjson.connexion.Requester +import io.cucumber.datatable.DataTable +import io.cucumber.java8.En +import io.ktor.http.HttpHeaders +import org.joda.time.DateTime +import org.koin.test.KoinTest +import org.koin.test.get +import org.koin.test.inject +import java.util.* +import kotlin.random.Random +import fr.dcproject.repository.User as UserRepository + +class KtorServerAuthSteps: En, KoinTest { + private val requester: Requester by inject() + init { + When("I have citizen:") { body: DataTable -> + val user = User(username = "jaque_${Random.nextInt(0, 10000)}", plainPassword = "azerty") + requester + .getFunction("insert_user") + .selectOne(user) + + val data = body.asMap(String::class.java, String::class.java) + val citizen = Citizen( + id = UUID.fromString(data["id"]), + name = Citizen.Name(data["firstName"], data["lastName"]), + birthday = DateTime.now(), + user = user + ) + requester + .getFunction("upsert_citizen") + .selectOne(citizen) + } + + Given("I am authenticated as an user") { + val id = UUID.randomUUID() + val jwtAsString: String = JWT.create() + .withIssuer("dc-project.fr") + .withClaim("id", id.toString()) + .sign(Algorithm.HMAC512(JwtConfig.secret)) + + val user = User(id = id, username = "user", plainPassword = "azerty") + get().insert(user) + + KtorServerContext.defaultServer.addPreRequestSetup { + addHeader(HttpHeaders.Authorization, "Bearer $jwtAsString") + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/feature/KtorServerContext.kt b/src/test/kotlin/feature/KtorServerContext.kt new file mode 100644 index 0000000..49751cf --- /dev/null +++ b/src/test/kotlin/feature/KtorServerContext.kt @@ -0,0 +1,54 @@ +package feature + +import io.ktor.application.Application +import io.ktor.server.testing.TestApplicationCall +import io.ktor.server.testing.TestApplicationEngine +import io.ktor.server.testing.TestApplicationRequest +import io.ktor.server.testing.createTestEnvironment +import java.util.concurrent.TimeUnit +import kotlin.test.fail + +class KtorServerContext(useByDefault: Boolean = true, val module: Application.() -> Unit) { + + init { if (useByDefault) setDefault() } + + companion object { + lateinit var defaultServer: KtorServerContext + } + + private val engine = TestApplicationEngine(createTestEnvironment()) + + private data class RequestSetup(val setup: TestApplicationRequest.() -> Unit, val keepSetup: Boolean = true) + private val preRequestSetup = mutableListOf() + + var call: TestApplicationCall? = null + + fun addPreRequestSetup(keepSetup: Boolean = true, hook: TestApplicationRequest.() -> Unit) { + preRequestSetup.add(RequestSetup(hook, keepSetup)) + } + + fun handleRequest(setup: TestApplicationRequest.() -> Unit) = + try { + call = engine.handleRequest { + preRequestSetup.forEach { it.setup(this) } + setup(this) + } + } catch (e: Throwable) { + fail("Request fail, $e") + } finally { + preRequestSetup.removeAll { !it.keepSetup } + } + + fun setDefault() { + defaultServer = this + } + + fun start() { + engine.start() + module(engine.application) + } + + fun stop() { + engine.stop(0L, 0L, TimeUnit.MILLISECONDS) + } +} diff --git a/src/test/kotlin/feature/KtorServerRequestSteps.kt b/src/test/kotlin/feature/KtorServerRequestSteps.kt new file mode 100644 index 0000000..8f880ea --- /dev/null +++ b/src/test/kotlin/feature/KtorServerRequestSteps.kt @@ -0,0 +1,64 @@ +package feature + +import com.google.gson.JsonParser +import io.cucumber.datatable.DataTable +import io.cucumber.java8.En +import io.ktor.http.ContentType +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpMethod +import io.ktor.http.HttpStatusCode +import io.ktor.server.testing.setBody +import io.ktor.util.KtorExperimentalAPI +import kotlinx.serialization.ImplicitReflectionSerializer +import org.junit.jupiter.api.Assertions +import org.opentest4j.AssertionFailedError +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +@ImplicitReflectionSerializer +@KtorExperimentalAPI +class KtorServerRequestSteps : En { + init { + Given("Next request as headers:") { dataTable: DataTable -> + KtorServerContext.defaultServer.addPreRequestSetup(false) { + dataTable.asMap(String::class.java, String::class.java).forEach { key, value -> + this.addHeader(key, value) + } + } + } + + Given("I send a {word} request to {string} with body:") { method: String, uri: String, body: String -> + KtorServerContext.defaultServer.handleRequest { + this.method = HttpMethod.parse(method) + this.uri = uri + this.addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString()) + setBody(body) + } + } + + Given("I send a {word} request to {string}") { method: String, uri: String -> + KtorServerContext.defaultServer.handleRequest { + this.method = HttpMethod.parse(method.toUpperCase()) + this.uri = uri + } + } + + Then("the response status code should be {int}") { statusCode: Int -> + assertEquals(HttpStatusCode.fromValue(statusCode), KtorServerContext.defaultServer.call?.response?.status()) + } + + Then("the response should contain object:") { expected: DataTable -> + val call = KtorServerContext.defaultServer.call ?: throw AssertionFailedError("No call") + val response = JsonParser().parse(call.response.content).getAsJsonObject() + + expected.asMap(String::class.java, String::class.java).forEach { (key, valueExpected) -> + assertTrue(response.has(key)) + Assertions.assertEquals(valueExpected, response.get(key).asString) + } + } + + Then("print last response") { + print(KtorServerContext.defaultServer.call?.response?.content) + } + } +} diff --git a/src/test/kotlin/feature/KtorServerRestSteps.kt b/src/test/kotlin/feature/KtorServerRestSteps.kt new file mode 100644 index 0000000..054034f --- /dev/null +++ b/src/test/kotlin/feature/KtorServerRestSteps.kt @@ -0,0 +1,55 @@ +package feature + +import io.cucumber.datatable.DataTable +import io.cucumber.java8.En +import kotlinx.serialization.ImplicitReflectionSerializer +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.parse +import kotlin.test.assertEquals +import kotlin.test.fail + +@ImplicitReflectionSerializer +class KtorServerRestSteps : En { + init { + Then("the JSON should contain:") { dataTable: DataTable -> + dataTable.asMap(String::class.java, String::class.java).forEach { (key, value) -> + val jsonPrimitive = findJsonElement(key) as? JsonPrimitive ?: fail("\"$key\" element isn't json primitive") + assertEquals(jsonPrimitive.content, value) + } + } + + Then("the JSON element {word} should have {int} item(s)") { node: String, count: Int -> + val jsonArray = findJsonElement(node) as? JsonArray ?: fail("\"$node\" element isn't json array") + assertEquals(count, jsonArray.size) + } + + Then("the JSON should have {int} item(s)") { count: Int -> + val jsonArray = responseJsonElement as? JsonArray ?: fail("The json response isn't array") + assertEquals(count, jsonArray.size) + } + } + + private fun findJsonElement(node: String): JsonElement { + var jsonElement: JsonElement = responseJsonElement + val elements = node.split(".") + + elements.forEach { + val asArrayIndex = """\d+""".toRegex().find(it) + + jsonElement = if (asArrayIndex != null) { + val index = asArrayIndex.groups.first()!! + jsonElement.jsonArray.get(index.value.toInt()) + } else { + jsonElement.jsonObject.get(it) ?: throw AssertionError("\"$node\" element not found on json response") + } + } + + return jsonElement + } + + private val responseJsonElement: JsonElement + get() = Json.parse(KtorServerContext.defaultServer.call?.response?.content ?: fail("The response isn't valid JSON")) +} diff --git a/src/test/kotlin/feature/Request.kt b/src/test/kotlin/feature/Request.kt deleted file mode 100644 index 03c18e3..0000000 --- a/src/test/kotlin/feature/Request.kt +++ /dev/null @@ -1,116 +0,0 @@ -package feature - -import com.google.gson.Gson -import com.google.gson.JsonParser -import cucumber.api.java8.En -import fr.dcproject.entity.Citizen -import fr.dcproject.entity.User -import fr.postgresjson.connexion.Requester -import fr.postgresjson.migration.Migrations -import io.cucumber.datatable.DataTable -import io.ktor.http.ContentType -import io.ktor.http.HttpHeaders -import io.ktor.http.HttpMethod -import io.ktor.http.HttpStatusCode -import io.ktor.server.testing.TestApplicationCall -import io.ktor.server.testing.TestApplicationEngine -import io.ktor.server.testing.setBody -import org.joda.time.DateTime -import org.junit.jupiter.api.Assertions.assertEquals -import org.koin.test.KoinTest -import org.koin.test.inject -import org.opentest4j.AssertionFailedError -import java.util.* -import kotlin.random.Random -import kotlin.test.assertTrue -import kotlin.test.asserter -import feature.Context.Companion.current as currentContext - -class Request: En, KoinTest { - private val migrations: Migrations by inject() - private val requester: Requester by inject() - init { - When("I have citizen:") { body: DataTable -> - val user = User(username = "jaque_${Random.nextInt(0, 10000)}", plainPassword = "azerty") - val test: TestApplicationEngine.() -> Unit = { - requester - .getFunction("insert_user") - .selectOne(user) - - val data = body.asMap(String::class.java, String::class.java) - val citizen = Citizen( - id = UUID.fromString(data["id"]), - name = Citizen.Name(data["firstName"], data["lastName"]), - birthday = DateTime.now(), - user = user - ) - requester - .getFunction("upsert_citizen") - .selectOne(citizen) - } - - currentContext.engine.test() - } - - When("I send a {string} request to {string} with body:") { method: String, uri: String, body: String -> - val test: TestApplicationEngine.() -> Unit = { - currentContext.call = handleRequest { - applyConfigurations() - addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString()) - this.method = HttpMethod.parse(method) - this.uri = uri - setBody(body) - } - } - - currentContext.engine.test() - } - - When("I send a {string} request to {string}") { method: String, uri: String -> - val test: TestApplicationEngine.() -> Unit = { - currentContext.call = handleRequest { - applyConfigurations() - addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString()) - this.method = HttpMethod.parse(method.toUpperCase()) - this.uri = uri - } - } - - currentContext.engine.test() - } - - Then("the response status code should be {int}") { statusCode: Int -> - val call: TestApplicationCall = currentContext.call ?: throw AssertionFailedError("No call", statusCode, null) - with(call) { - assertEquals(HttpStatusCode.fromValue(statusCode), response.status(), response.content) - } - } - - And("the response should contain:") { expected: DataTable -> - val call: TestApplicationCall = currentContext.call ?: throw AssertionFailedError("No call") - val p = call.response - val response = Gson().fromJson>>(p.content, List::class.java) - - expected.asMap(String::class.java, String::class.java).forEach { (key, value) -> - response.forEach { - if (it.containsKey(key)) { - assertEquals(it[key], value) - return@And - } - } - asserter.fail("The response not contain $key field") - } - } - - And("the response should contain object:") { expected: DataTable -> - val call: TestApplicationCall = currentContext.call ?: throw AssertionFailedError("No call") - val p = call.response - val response = JsonParser().parse(p.content).getAsJsonObject() - - expected.asMap(String::class.java, String::class.java).forEach { (key, valueExpected) -> - assertTrue(response.has(key)) - assertEquals(valueExpected, response.get(key).asString) - } - } - } -} \ No newline at end of file diff --git a/src/test/resources/feature/articles.feature b/src/test/resources/feature/articles.feature index 305c1f2..9980a7f 100644 --- a/src/test/resources/feature/articles.feature +++ b/src/test/resources/feature/articles.feature @@ -1,11 +1,11 @@ Feature: articles routes Scenario: The route for get articles must response a 200 - When I send a "GET" request to "/articles" + When I send a GET request to "/articles" Then the response status code should be 200 Scenario: The route for get one article must response a 200 and return article - When I send a "GET" request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b" + When I send a GET request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b" Then the response status code should be 200 And the response should contain object: | id | 9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b | @@ -15,7 +15,8 @@ Feature: articles routes | id | 64b7b379-2298-43ec-b428-ba134930cabd | | firstName | Jaque | | lastName | Dupuis | - When I send a "POST" request to "/articles" with body: + And I am authenticated as an user + When I send a POST request to "/articles" with body: """ { "version_id": "09c418b6-63ba-448b-b38b-502b41cd500e", diff --git a/src/test/resources/feature/citizen.feature b/src/test/resources/feature/citizen.feature index 6923db5..26bbff4 100644 --- a/src/test/resources/feature/citizen.feature +++ b/src/test/resources/feature/citizen.feature @@ -1,11 +1,11 @@ Feature: citizens routes Scenario: The route for get citizens must response a 200 - When I send a "GET" request to "/citizens" + When I send a GET request to "/citizens" Then the response status code should be 200 Scenario: The route for get one citizen must response a 200 and return citizen - When I send a "GET" request to "/citizens/6434f4f9-f570-f22a-c134-8668350651ff" + When I send a GET request to "/citizens/6434f4f9-f570-f22a-c134-8668350651ff" Then the response status code should be 200 And the response should contain object: | id | 6434f4f9-f570-f22a-c134-8668350651ff | diff --git a/src/test/resources/feature/constitution.feature b/src/test/resources/feature/constitution.feature index 2289d5e..a03313b 100644 --- a/src/test/resources/feature/constitution.feature +++ b/src/test/resources/feature/constitution.feature @@ -1,11 +1,11 @@ Feature: constitution routes Scenario: The route for get constitutions must response a 200 - When I send a "GET" request to "/constitutions" + When I send a GET request to "/constitutions" Then the response status code should be 200 Scenario: The route for get one constitution must response a 200 and return constitution - When I send a "GET" request to "/constitutions/0ca489a6-ef68-8bd5-2355-5793d4b3d66c" + When I send a GET request to "/constitutions/0ca489a6-ef68-8bd5-2355-5793d4b3d66c" Then the response status code should be 200 And the response should contain object: | id | 0ca489a6-ef68-8bd5-2355-5793d4b3d66c | @@ -15,7 +15,7 @@ Feature: constitution routes | id | 64b7b379-2298-43ec-b428-ba134930cabd | | firstName | Jaque | | lastName | Dupuis | - When I send a "POST" request to "/constitutions" with body: + When I send a POST request to "/constitutions" with body: """ { "version_id":"15814bb6-8d90-4c6a-a456-c3939a8ec75e", diff --git a/src/test/resources/feature/follow.feature b/src/test/resources/feature/follow.feature index f985381..dd9eeb1 100644 --- a/src/test/resources/feature/follow.feature +++ b/src/test/resources/feature/follow.feature @@ -6,7 +6,7 @@ Feature: follow Article and Constitution | id | 64b7b379-2298-43ec-b428-ba134930cabd | | firstName | Jaque | | lastName | Dupuis | - When I send a "POST" request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/follow" + When I send a POST request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/follow" Then the response status code should be 201 Scenario: The route for get follows of articles must response a 200 and return objects @@ -14,7 +14,7 @@ Feature: follow Article and Constitution | id | 64b7b379-2298-43ec-b428-ba134930cabd | | firstName | Jaque | | lastName | Dupuis | - When I send a "GET" request to "/citizens/64b7b379-2298-43ec-b428-ba134930cabd/follows/articles" + When I send a GET request to "/citizens/64b7b379-2298-43ec-b428-ba134930cabd/follows/articles" Then the response status code should be 200 And the response should contain object: | current_page | 1 | @@ -25,7 +25,7 @@ Feature: follow Article and Constitution | id | 64b7b379-2298-43ec-b428-ba134930cabd | | firstName | Jaque | | lastName | Dupuis | - When I send a "DELETE" request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/follow" + When I send a DELETE request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/follow" Then the response status code should be 204 # Constitution @@ -34,7 +34,7 @@ Feature: follow Article and Constitution | id | 64b7b379-2298-43ec-b428-ba134930cabd | | firstName | Jaque | | lastName | Dupuis | - When I send a "POST" request to "/constitutions/72aa1ee1-4963-eb44-c9e0-5ce6e0f18f00/follow" + When I send a POST request to "/constitutions/72aa1ee1-4963-eb44-c9e0-5ce6e0f18f00/follow" Then the response status code should be 201 Scenario: The route for get follows of constitutions must response a 200 and return objects @@ -42,7 +42,7 @@ Feature: follow Article and Constitution | id | 64b7b379-2298-43ec-b428-ba134930cabd | | firstName | Jaque | | lastName | Dupuis | - When I send a "GET" request to "/citizens/64b7b379-2298-43ec-b428-ba134930cabd/follows/constitutions" + When I send a GET request to "/citizens/64b7b379-2298-43ec-b428-ba134930cabd/follows/constitutions" Then the response status code should be 200 And the response should contain object: | current_page | 1 | @@ -53,5 +53,5 @@ Feature: follow Article and Constitution | id | 64b7b379-2298-43ec-b428-ba134930cabd | | firstName | Jaque | | lastName | Dupuis | - When I send a "DELETE" request to "/constitutions/72aa1ee1-4963-eb44-c9e0-5ce6e0f18f00/follow" + When I send a DELETE request to "/constitutions/72aa1ee1-4963-eb44-c9e0-5ce6e0f18f00/follow" Then the response status code should be 204