feature: #6: Add cucumber test for article routes
This commit is contained in:
9
.idea/compiler.xml
generated
9
.idea/compiler.xml
generated
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel>
|
||||
<module name="dcproject.main" target="11" />
|
||||
<module name="dcproject.test" target="11" />
|
||||
</bytecodeTargetLevel>
|
||||
</component>
|
||||
</project>
|
||||
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
|
||||
@@ -38,6 +38,14 @@ dependencies {
|
||||
implementation("net.pearx.kasechange:kasechange-jvm:1.1.0")
|
||||
implementation("fr.postgresjson:postgresjson:$postgresjson_version")
|
||||
testImplementation("io.ktor:ktor-server-tests:$ktor_version")
|
||||
testImplementation("io.ktor:ktor-client-mock:$ktor_version")
|
||||
testImplementation("io.ktor:ktor-client-mock-jvm:$ktor_version")
|
||||
testImplementation("org.koin:koin-test:$koinVersion")
|
||||
testImplementation("io.mockk:mockk:1.9")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.5.0")
|
||||
testImplementation("org.amshove.kluent:kluent:1.4")
|
||||
testImplementation("io.cucumber:cucumber-java8:4.3.1")
|
||||
testImplementation("io.cucumber:cucumber-junit:4.3.1")
|
||||
}
|
||||
|
||||
kotlin.sourceSets["main"].kotlin.srcDirs("src")
|
||||
|
||||
@@ -6,9 +6,10 @@ import org.koin.dsl.module
|
||||
import java.io.File
|
||||
import fr.dcproject.repository.Article as ArticleRepository
|
||||
|
||||
val config = Config()
|
||||
|
||||
@KtorExperimentalAPI
|
||||
val Module = module {
|
||||
val config = Config()
|
||||
|
||||
single { config }
|
||||
|
||||
|
||||
@@ -21,10 +21,17 @@ class Article(override var requester: Requester) : RepositoryI<ArticleEntity> {
|
||||
}
|
||||
}
|
||||
|
||||
fun find(page: Int = 1, limit: Int = 50, sort: String? = null, direction: Direction? = null, search: String? = null): Paginated<ArticleEntity> {
|
||||
fun find(
|
||||
page: Int = 1,
|
||||
limit: Int = 50,
|
||||
sort: String? = null,
|
||||
direction: Direction? = null,
|
||||
search: String? = null
|
||||
): Paginated<ArticleEntity> {
|
||||
return requester
|
||||
.getFunction("find_articles")
|
||||
.select(page, limit,
|
||||
.select(
|
||||
page, limit,
|
||||
"sort" to sort?.toSnakeCase(),
|
||||
"direction" to direction,
|
||||
"search" to search
|
||||
|
||||
25
test/RunCucumberTest.kt
Normal file
25
test/RunCucumberTest.kt
Normal file
@@ -0,0 +1,25 @@
|
||||
import cucumber.api.CucumberOptions
|
||||
import cucumber.api.Scenario
|
||||
import cucumber.api.java8.En
|
||||
import cucumber.api.junit.Cucumber
|
||||
import feature.Context
|
||||
import io.ktor.server.testing.TestApplicationEngine
|
||||
import io.ktor.server.testing.createTestEnvironment
|
||||
import org.junit.runner.RunWith
|
||||
import java.util.concurrent.TimeUnit
|
||||
import feature.Context.Companion.current as contextCurrent
|
||||
|
||||
@RunWith(Cucumber::class)
|
||||
@CucumberOptions(plugin = ["pretty"])
|
||||
class RunCucumberTest: En {
|
||||
init {
|
||||
Before(-1) { scenario: Scenario ->
|
||||
// config.database = "dc-projectg-test"
|
||||
contextCurrent = Context(TestApplicationEngine(createTestEnvironment()) {}, scenario)
|
||||
}
|
||||
|
||||
After { scenario: Scenario ->
|
||||
contextCurrent.engine.stop(0L, 0L, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
}
|
||||
}
|
||||
40
test/feature/Context.kt
Normal file
40
test/feature/Context.kt
Normal file
@@ -0,0 +1,40 @@
|
||||
package feature
|
||||
|
||||
import cucumber.api.Scenario
|
||||
import fr.dcproject.module
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.server.testing.TestApplicationCall
|
||||
import io.ktor.server.testing.TestApplicationEngine
|
||||
import io.ktor.server.testing.TestApplicationRequest
|
||||
|
||||
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<TestApplicationRequest.() -> 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)
|
||||
}
|
||||
92
test/feature/Request.kt
Normal file
92
test/feature/Request.kt
Normal file
@@ -0,0 +1,92 @@
|
||||
package feature
|
||||
|
||||
import com.google.gson.Gson
|
||||
import cucumber.api.Scenario
|
||||
import cucumber.api.java8.En
|
||||
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.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.koin.test.KoinTest
|
||||
import org.opentest4j.AssertionFailedError
|
||||
import kotlin.test.asserter
|
||||
import feature.Context.Companion.current as currentContext
|
||||
|
||||
class Request: En, KoinTest {
|
||||
init {
|
||||
Before { scenario: Scenario ->
|
||||
}
|
||||
|
||||
After { scenario: Scenario ->
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
And("the response should contain:") { expected: DataTable ->
|
||||
val call: TestApplicationCall = currentContext.call ?: throw AssertionFailedError("No call")
|
||||
val p = call.response
|
||||
val response = Gson().fromJson<List<Map<String, String>>>(p.content, List::class.java)
|
||||
|
||||
expected.asMap<String, String>(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 = Gson().fromJson<Map<String, String>>(p.content, Map::class.java)
|
||||
|
||||
expected.asMap<String, String>(String::class.java, String::class.java).forEach { (key, value) ->
|
||||
if (response.containsKey(key)) {
|
||||
assertEquals(value, response[key])
|
||||
return@And
|
||||
}
|
||||
asserter.fail("The response not contain $key field")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
testresources/feature/articles.feature
Normal file
36
testresources/feature/articles.feature
Normal file
@@ -0,0 +1,36 @@
|
||||
Feature: articles routes
|
||||
|
||||
Scenario: The route for get articles must response a 200
|
||||
When I send a "GET" request to "/articles"
|
||||
Then the response status code should be 200
|
||||
|
||||
Scenario: The route for get article must response a 200
|
||||
When I send a "GET" request to "/articles/55a24426-139b-4ee7-b1e2-a3d016d66cc2"
|
||||
Then the response status code should be 200
|
||||
|
||||
Scenario: The route for get article must response a 200
|
||||
When I send a "POST" request to "/articles" with body:
|
||||
"""
|
||||
{
|
||||
"version_id": "09c418b6-63ba-448b-b38b-502b41cd500e",
|
||||
"title": "title2",
|
||||
"annonymous": false,
|
||||
"content": "content2",
|
||||
"description": "description2",
|
||||
"tags": [
|
||||
"green"
|
||||
],
|
||||
"created_by": {
|
||||
"id": "64b7b379-2298-43ec-b428-ba134930cabd"
|
||||
}
|
||||
}
|
||||
"""
|
||||
Then the response status code should be 200
|
||||
And the response should contain object:
|
||||
| version_id | 09c418b6-63ba-448b-b38b-502b41cd500e |
|
||||
| title | title2 |
|
||||
When I send a "GET" request to "/articles/99afd1b1-3555-43c1-80a7-63c56e93d250"
|
||||
Then the response status code should be 200
|
||||
And the response should contain object:
|
||||
| id | 99afd1b1-3555-43c1-80a7-63c56e93d250 |
|
||||
| title | title2 |
|
||||
Reference in New Issue
Block a user