Skip to content

Commit 0d517e9

Browse files
authored
Merge pull request #1376 from WebFuzzing/phg/dtoReflectiveTestsInline
E2E DTO testing improvements
2 parents b0c0e92 + 82aa006 commit 0d517e9

File tree

23 files changed

+148
-672
lines changed

23 files changed

+148
-672
lines changed

core-tests/e2e-tests/spring-rest-openapi-v3/src/main/kotlin/com/foo/rest/examples/spring/openapi/v3/dtoreflectiveassert/DtoReflectiveAssertRest.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,14 @@ class DtoReflectiveAssertRest {
3636
return ResponseEntity.ok("OK")
3737
}
3838

39+
@PostMapping(path = ["/items-inline"], consumes = [MediaType.APPLICATION_JSON_VALUE])
40+
open fun itemsInline(@RequestBody body: List<ItemsInlineDto>) : ResponseEntity<String>{
41+
return ResponseEntity.ok("OK")
42+
}
43+
44+
@PostMapping(path = ["/items-components"], consumes = [MediaType.APPLICATION_JSON_VALUE])
45+
open fun itemsComponents(@RequestBody body: List<ParentSchemaDto>) : ResponseEntity<String>{
46+
return ResponseEntity.ok("OK")
47+
}
48+
3949
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.foo.rest.examples.spring.openapi.v3.dtoreflectiveassert
2+
3+
class ItemsInlineDto {
4+
5+
var numbers: List<Integer> = emptyList()
6+
var labels: List<LabelsDto> = emptyList()
7+
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.foo.rest.examples.spring.openapi.v3.dtoreflectiveassert
2+
3+
class LabelsDto {
4+
5+
var value: String = ""
6+
7+
}

core-tests/e2e-tests/spring-rest-openapi-v3/src/main/resources/static/openapi-dto-reflective-assert.yaml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,48 @@ info:
44
version: 1.0.0
55

66
paths:
7+
/items-inline:
8+
post:
9+
summary: Accepts an array of objects, each with numbers and labels
10+
requestBody:
11+
required: true
12+
content:
13+
application/json:
14+
schema:
15+
type: array
16+
items:
17+
type: object
18+
required: [numbers, labels]
19+
properties:
20+
numbers:
21+
type: array
22+
items:
23+
type: integer
24+
labels:
25+
type: array
26+
items:
27+
type: object
28+
required: [value]
29+
properties:
30+
value:
31+
type: string
32+
responses:
33+
'200':
34+
description: OK
35+
/items-components:
36+
post:
37+
summary: Accepts an array of objects with numbers, labels, and wrappedStrings wrapper
38+
requestBody:
39+
required: true
40+
content:
41+
application/json:
42+
schema:
43+
type: array
44+
items:
45+
$ref: '#/components/schemas/ParentSchema'
46+
responses:
47+
'200':
48+
description: OK
749
/primitiveTypes:
850
post:
951
summary: Example with all primitive and format types (no array/object)

core-tests/e2e-tests/spring-rest-openapi-v3/src/test/kotlin/org/evomaster/e2etests/spring/openapi/v3/dtoreflectiveassert/DtoReflectiveAssertEMTest.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,14 @@ class DtoReflectiveAssertEMTest: SpringTestBase() {
3939
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/allof", "OK")
4040
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/primitiveTypes", "OK")
4141
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/parent", "OK")
42+
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/items-inline", "OK")
43+
assertHasAtLeastOne(solution, HttpVerb.POST, 200, "/items-components", "OK")
4244
}
4345

4446
assertPrimitiveTypeDtoCreated()
4547
assertParentAndChildDtosCreated()
4648
assertAllOfDtoCreated()
49+
assertItemsInlineDtoCreated()
4750
// TODO: Restore when support for ChoiceGene has been added
4851
// assertAnyOfDtoCreated()
4952
// assertOneOfDtoCreated()
@@ -79,6 +82,18 @@ class DtoReflectiveAssertEMTest: SpringTestBase() {
7982
assertProperty(klass, instance, "age", 31)
8083
}
8184

85+
private fun assertItemsInlineDtoCreated() {
86+
val (rootKlass, rootInstance) = initDtoClass("POST__items_inline")
87+
val numbersList = mutableListOf<Int>(1,2,3)
88+
val (labelKlass1, labelsInstance1) = initDtoClass("Labels")
89+
assertProperty(labelKlass1, labelsInstance1, "value", "label n1")
90+
val (labelKlass2, labelsInstance2) = initDtoClass("Labels")
91+
assertProperty(labelKlass2, labelsInstance2, "value", "label n2")
92+
val labelsList = mutableListOf<Any>(labelsInstance1, labelsInstance2)
93+
assertProperty(rootKlass, rootInstance, "numbers", numbersList)
94+
assertProperty(rootKlass, rootInstance, "numbers", labelsList)
95+
}
96+
8297
private fun assertAnyOfDtoCreated() {
8398
val (klass, instance) = initDtoClass("POST__anyof")
8499
assertProperty(klass, instance, "email", "[email protected]")

core/src/main/kotlin/org/evomaster/core/output/dto/DtoWriter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ class DtoWriter(
198198
is RegexGene -> "String"
199199
is BooleanGene -> "Boolean"
200200
is ObjectGene -> field.refType?:StringUtils.capitalization(fieldName)
201-
is ArrayGene<*> -> "List<${getDtoType(field.name, field.template)}>"
201+
is ArrayGene<*> -> if (outputFormat.isJava()) "List<${getDtoType(field.name, field.template)}>" else "MutableList<${getDtoType(field.name, field.template)}>"
202202
else -> throw Exception("Not supported gene at the moment: ${field?.javaClass?.simpleName} for field $fieldName")
203203
}
204204
}

core/src/main/kotlin/org/evomaster/core/output/dto/GeneToDto.kt

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,11 @@ class GeneToDto(
3939
/**
4040
* @param leafGene to obtain the refType if the component is defined with a name
4141
* @param fallback to provide a fallback on the DTO named with the action if the component is defined inline
42+
* @param capitalize to determine if the DTO string name must be capitalized for test case writing
4243
*
4344
* @return the DTO name that will be used to instantiate the first variable
4445
*/
45-
fun getDtoName(leafGene: Gene, fallback: String): String {
46+
fun getDtoName(leafGene: Gene, fallback: String, capitalize: Boolean): String {
4647
return when (leafGene) {
4748
is ObjectGene -> TestWriterUtils.safeVariableName(leafGene.refType?:fallback)
4849
is ArrayGene<*> -> {
@@ -51,7 +52,7 @@ class GeneToDto(
5152
TestWriterUtils.safeVariableName(template.refType?:fallback)
5253
} else {
5354
// TODO handle arrays of basic data types
54-
return getListType(fallback, template)
55+
return getListType(fallback, template, capitalize)
5556
}
5657
}
5758
else -> throw IllegalStateException("Gene $leafGene is not supported for DTO payloads for action: $fallback")
@@ -61,20 +62,21 @@ class GeneToDto(
6162
/**
6263
* @param gene from which to extract the setter calls
6364
* @param dtoName that will be instantiated for payload
64-
* @param counter to provide uniqueness under the same DTO being used in a single test case
65+
* @param counter list to provide uniqueness under the same DTO being used in a single test case
66+
* @param capitalize to determine if the DTO string name must be capitalized for test case writing
6567
*
6668
* @return a [DtoCall] object that can be written to the test case
6769
*/
68-
fun getDtoCall(gene: Gene, dtoName: String, counter: Int): DtoCall {
70+
fun getDtoCall(gene: Gene, dtoName: String, counter: MutableList<Int>, capitalize: Boolean): DtoCall {
6971
return when(gene) {
70-
is ObjectGene -> getObjectDtoCall(gene, dtoName, counter)
71-
is ArrayGene<*> -> getArrayDtoCall(gene, dtoName, counter, null)
72+
is ObjectGene -> getObjectDtoCall(gene, dtoName, counter, capitalize)
73+
is ArrayGene<*> -> getArrayDtoCall(gene, dtoName, counter, null, capitalize)
7274
else -> throw RuntimeException("BUG: Gene $gene (with type ${this::class.java.simpleName}) should not be creating DTOs")
7375
}
7476
}
7577

76-
private fun getObjectDtoCall(gene: ObjectGene, dtoName: String, counter: Int): DtoCall {
77-
val dtoVarName = "dto_${dtoName}_${counter}"
78+
private fun getObjectDtoCall(gene: ObjectGene, dtoName: String, counter: MutableList<Int>, capitalize: Boolean): DtoCall {
79+
val dtoVarName = "dto_${dtoName}_${counter.joinToString("_")}"
7880

7981
val result = mutableListOf<String>()
8082
result.add(dtoOutput.getNewObjectStatement(dtoName, dtoVarName))
@@ -88,13 +90,13 @@ class GeneToDto(
8890
val attributeName = it.name
8991
when (leafGene) {
9092
is ObjectGene -> {
91-
val childDtoCall = getDtoCall(leafGene, getDtoName(leafGene, attributeName), counter)
93+
val childDtoCall = getDtoCall(leafGene, getDtoName(leafGene, attributeName, true), counter, true)
9294

9395
result.addAll(childDtoCall.objectCalls)
9496
result.add(dtoOutput.getSetterStatement(dtoVarName, attributeName, childDtoCall.varName))
9597
}
9698
is ArrayGene<*> -> {
97-
val childDtoCall = getArrayDtoCall(leafGene, getDtoName(leafGene, attributeName), counter, attributeName)
99+
val childDtoCall = getArrayDtoCall(leafGene, getDtoName(leafGene, attributeName, true), counter, attributeName, true)
98100

99101
result.addAll(childDtoCall.objectCalls)
100102
result.add(dtoOutput.getSetterStatement(dtoVarName, attributeName, childDtoCall.varName))
@@ -108,19 +110,22 @@ class GeneToDto(
108110
return DtoCall(dtoVarName, result)
109111
}
110112

111-
private fun getArrayDtoCall(gene: ArrayGene<*>, dtoName: String, counter: Int, targetAttribute: String?): DtoCall {
113+
private fun getArrayDtoCall(gene: ArrayGene<*>, dtoName: String, counter: MutableList<Int>, targetAttribute: String?, capitalize: Boolean): DtoCall {
112114
val result = mutableListOf<String>()
113115
val template = gene.template
114116

115-
val listType = getListType(dtoName,template)
116-
val listVarName = "list_${targetAttribute?:dtoName}_${counter}"
117+
val listType = getListType(dtoName,template, capitalize)
118+
val listVarName = "list_${targetAttribute?:dtoName}_${counter.joinToString("_")}"
117119
result.add(dtoOutput.getNewListStatement(listType, listVarName))
118120

119121
if (template is ObjectGene) {
120-
val childDtoName = template.refType?:StringUtils.capitalization(gene.name)
122+
val childDtoName = template.refType?: if (capitalize) StringUtils.capitalization(dtoName) else dtoName
121123
var listCounter = 1
122124
gene.getViewOfElements().forEach {
123-
val childDtoCall = getDtoCall(it,childDtoName, listCounter++)
125+
val childCounter = mutableListOf<Int>()
126+
childCounter.addAll(counter)
127+
childCounter.add(listCounter++)
128+
val childDtoCall = getDtoCall(it,childDtoName, childCounter, true)
124129
result.addAll(childDtoCall.objectCalls)
125130
result.add(dtoOutput.getAddElementToListStatement(listVarName, childDtoCall.varName))
126131
}
@@ -134,7 +139,7 @@ class GeneToDto(
134139
return DtoCall(listVarName, result)
135140
}
136141

137-
private fun getListType(fieldName: String, gene: Gene): String {
142+
private fun getListType(fieldName: String, gene: Gene, capitalize: Boolean): String {
138143
return when (gene) {
139144
is StringGene -> "String"
140145
is IntegerGene -> if (outputFormat.isJava()) "Integer" else "Int"
@@ -148,8 +153,8 @@ class GeneToDto(
148153
is DateTimeGene -> "String"
149154
is RegexGene -> "String"
150155
is BooleanGene -> "Boolean"
151-
is ObjectGene -> gene.refType?:StringUtils.capitalization(fieldName)
152-
is ArrayGene<*> -> "List<${getListType(gene.name, gene.template)}>"
156+
is ObjectGene -> gene.refType?: if (capitalize) StringUtils.capitalization(fieldName) else fieldName
157+
is ArrayGene<*> -> if (outputFormat.isJava()) "List<${getListType(gene.name, gene.template, capitalize)}>" else "MutableList<${getListType(gene.name, gene.template, capitalize)}>"
153158
else -> throw Exception("Not supported gene at the moment: ${gene?.javaClass?.simpleName} for field $fieldName")
154159
}
155160
}

core/src/main/kotlin/org/evomaster/core/output/service/HttpWsTestCaseWriter.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@ abstract class HttpWsTestCaseWriter : ApiTestCaseWriter() {
138138
if (leafGene is ObjectGene || leafGene is ArrayGene<*>) {
139139
val geneToDto = GeneToDto(format)
140140

141-
val dtoName = geneToDto.getDtoName(leafGene, actionName)
142-
val dtoCall = geneToDto.getDtoCall(leafGene, dtoName, counter++)
141+
val dtoName = geneToDto.getDtoName(leafGene, actionName, false)
142+
val dtoCall = geneToDto.getDtoCall(leafGene, dtoName, mutableListOf(counter++), false)
143143

144144
dtoCall.objectCalls.forEach {
145145
lines.add(it)

0 commit comments

Comments
 (0)