Skip to content

Commit 1e571a9

Browse files
Support Spring autowire two and more different beans of one type #2389 (#2421)
--------- Co-authored-by: Egor Kulikov <egor.k.kulikov@gmail.com>
1 parent 19fc16c commit 1e571a9

File tree

5 files changed

+71
-28
lines changed

5 files changed

+71
-28
lines changed

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractSpringTestClassConstructor.kt

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,13 @@ import org.utbot.framework.codegen.domain.models.CgStaticsRegion
1616
import org.utbot.framework.codegen.domain.models.CgVariable
1717
import org.utbot.framework.codegen.domain.models.SpringTestClassModel
1818
import org.utbot.framework.codegen.domain.models.builders.TypedModelWrappers
19-
import org.utbot.framework.plugin.api.ClassId
2019
import org.utbot.framework.plugin.api.UtExecution
2120
import org.utbot.framework.plugin.api.UtSpringContextModel
2221
import org.utbot.framework.plugin.api.util.SpringModelUtils.getBeanNameOrNull
2322
import org.utbot.framework.plugin.api.util.id
2423
import java.lang.Exception
2524

26-
abstract class CgAbstractSpringTestClassConstructor(context: CgContext):
25+
abstract class CgAbstractSpringTestClassConstructor(context: CgContext) :
2726
CgAbstractTestClassConstructor<SpringTestClassModel>(context) {
2827

2928
protected val variableConstructor: CgSpringVariableConstructor =
@@ -93,29 +92,43 @@ abstract class CgAbstractSpringTestClassConstructor(context: CgContext):
9392
open fun constructAdditionalUtilMethods(): CgMethodsCluster? = null
9493

9594
protected fun constructFieldsWithAnnotation(
96-
annotationClassId: ClassId,
95+
fieldManager: CgClassFieldManager,
9796
groupedModelsByClassId: TypedModelWrappers,
9897
): List<CgFieldDeclaration> {
98+
val annotationClassId = fieldManager.annotationType
9999
val annotation = addAnnotation(annotationClassId, Field)
100100

101101
val constructedDeclarations = mutableListOf<CgFieldDeclaration>()
102-
for ((classId, listOfUtModels) in groupedModelsByClassId) {
103-
val modelWrapper = listOfUtModels.firstOrNull() ?: continue
102+
for ((classId, modelWrappers) in groupedModelsByClassId) {
103+
104+
val fieldWithAnnotationIsRequired = fieldManager.fieldWithAnnotationIsRequired(modelWrappers)
105+
if (!fieldWithAnnotationIsRequired) {
106+
continue
107+
}
108+
109+
val modelWrapper = modelWrappers.firstOrNull() ?: continue
104110
val model = modelWrapper.model
111+
105112
val baseVarName = model.getBeanNameOrNull()
106113

107114
val createdVariable = variableConstructor.getOrCreateVariable(model, baseVarName) as? CgVariable
108115
?: error("`UtCompositeModel` model was expected, but $model was found")
109116

110117
val declaration = CgDeclaration(classId, variableName = createdVariable.name, initializer = null)
111-
constructedDeclarations += CgFieldDeclaration(ownerClassId = currentTestClass, declaration, annotation)
112118

113-
listOfUtModels.forEach { key ->
114-
valueByUtModelWrapper[key] = createdVariable
115-
}
116-
117-
variableConstructor.annotatedModelGroups
118-
.getOrPut(annotationClassId) { mutableSetOf() } += listOfUtModels
119+
constructedDeclarations += CgFieldDeclaration(
120+
ownerClassId = currentTestClass,
121+
declaration,
122+
annotation
123+
)
124+
125+
modelWrappers
126+
.forEach { modelWrapper ->
127+
modelWrapper.let {
128+
valueByUtModelWrapper[modelWrapper] = createdVariable
129+
variableConstructor.annotatedModelGroups.getOrPut(annotationClassId) { mutableSetOf() } += modelWrapper
130+
}
131+
}
119132
}
120133

121134
return constructedDeclarations

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgClassFieldManager.kt

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@ import org.utbot.framework.plugin.api.UtModel
1414
import org.utbot.framework.plugin.api.isMockModel
1515
import org.utbot.framework.plugin.api.util.SpringModelUtils.autowiredClassId
1616
import org.utbot.framework.plugin.api.util.SpringModelUtils.isAutowiredFromContext
17+
import java.util.*
18+
import kotlin.collections.HashMap
1719

1820
sealed interface CgClassFieldManager : CgContextOwner {
1921

2022
val annotationType: ClassId
2123

2224
fun constructVariableForField(model: UtModel, modelVariable: CgValue): CgValue
25+
26+
fun fieldWithAnnotationIsRequired(modelWrappers: Set<UtModelWrapper>): Boolean
2327
}
2428

2529
abstract class CgClassFieldManagerImpl(context: CgContext) :
@@ -29,6 +33,11 @@ abstract class CgClassFieldManagerImpl(context: CgContext) :
2933
val variableConstructor: CgSpringVariableConstructor by lazy {
3034
CgComponents.getVariableConstructorBy(context) as CgSpringVariableConstructor
3135
}
36+
37+
fun findCgValueByModel(model: UtModel, setOfModels: Set<UtModelWrapper>?): CgValue? {
38+
val key = setOfModels?.find { it == model.wrap() } ?: return null
39+
return valueByUtModelWrapper[key]
40+
}
3241
}
3342

3443
class CgInjectingMocksFieldsManager(val context: CgContext) : CgClassFieldManagerImpl(context) {
@@ -43,19 +52,23 @@ class CgInjectingMocksFieldsManager(val context: CgContext) : CgClassFieldManage
4352
}
4453

4554
modelFields?.forEach { (fieldId, fieldModel) ->
46-
//creating variables for modelVariable fields
55+
// creating variables for modelVariable fields
4756
val variableForField = variableConstructor.getOrCreateVariable(fieldModel)
4857

49-
// If field model is a mock, it is set in the connected with instance under test automatically via @InjectMocks;
58+
// is variable mocked by @Mock annotation
59+
val isMocked = findCgValueByModel(fieldModel, variableConstructor.annotatedModelGroups[mockClassId]) != null
60+
61+
// If field model is a mock model and is mocked by @Mock annotation in classFields, it is set in the connected with instance under test automatically via @InjectMocks;
5062
// Otherwise we need to set this field manually.
51-
if (!fieldModel.isMockModel()) {
63+
if (!fieldModel.isMockModel() || !isMocked) {
5264
variableConstructor.setFieldValue(modelVariable, fieldId, variableForField)
5365
}
5466
}
5567

5668
return modelVariable
5769
}
5870

71+
override fun fieldWithAnnotationIsRequired(modelWrappers: Set<UtModelWrapper>): Boolean = true
5972
}
6073

6174
class CgMockedFieldsManager(context: CgContext) : CgClassFieldManagerImpl(context) {
@@ -72,6 +85,20 @@ class CgMockedFieldsManager(context: CgContext) : CgClassFieldManagerImpl(contex
7285
return modelVariable
7386
}
7487

88+
override fun fieldWithAnnotationIsRequired(modelWrappers: Set<UtModelWrapper>): Boolean {
89+
// group [listOfUtModels] by `testSetId` and `executionId`
90+
// to check how many instances of one type are used in each execution
91+
val modelsByExecutions = modelWrappers
92+
.groupByTo(HashMap()) { Pair(it.testSetId, it.executionId) }
93+
94+
// maximal instances of one type amount in one execution
95+
val instanceMaxCount = Collections.max(modelsByExecutions.map { (_, modelsList) -> modelsList.size })
96+
97+
// if [instanceCount] is 1, then we mock variable by @Mock annotation
98+
// Otherwise we will mock variable by simple mock later
99+
return instanceMaxCount == 1
100+
}
101+
75102
}
76103

77104
class CgAutowiredFieldsManager(context: CgContext) : CgClassFieldManagerImpl(context) {
@@ -87,6 +114,8 @@ class CgAutowiredFieldsManager(context: CgContext) : CgClassFieldManagerImpl(con
87114
else -> error("Trying to autowire model $model but it is not appropriate")
88115
}
89116
}
117+
118+
override fun fieldWithAnnotationIsRequired(modelWrappers: Set<UtModelWrapper>): Boolean = true
90119
}
91120

92121
class ClassFieldManagerFacade(context: CgContext) : CgContextOwner by context {
@@ -97,12 +126,13 @@ class ClassFieldManagerFacade(context: CgContext) : CgContextOwner by context {
97126

98127
fun constructVariableForField(
99128
model: UtModel,
100-
annotatedModelGroups: Map<ClassId, Set<UtModelWrapper>>,
101129
): CgValue? {
102130
val annotationManagers = listOf(injectingMocksFieldsManager, mockedFieldsManager, autowiredFieldsManager)
103131

104132
annotationManagers.forEach { manager ->
105-
val alreadyCreatedVariable = findCgValueByModel(model, annotatedModelGroups[manager.annotationType])
133+
val annotatedModelGroups = manager.variableConstructor.annotatedModelGroups
134+
135+
val alreadyCreatedVariable = manager.findCgValueByModel(model, annotatedModelGroups[manager.annotationType])
106136

107137
if (alreadyCreatedVariable != null) {
108138
return manager.constructVariableForField(model, alreadyCreatedVariable)
@@ -111,9 +141,4 @@ class ClassFieldManagerFacade(context: CgContext) : CgContextOwner by context {
111141

112142
return null
113143
}
114-
115-
private fun findCgValueByModel(model: UtModel, setOfModels: Set<UtModelWrapper>?): CgValue? {
116-
val key = setOfModels?.find { it == model.wrap() } ?: return null
117-
return valueByUtModelWrapper[key]
118-
}
119144
}

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringIntegrationTestClassConstructor.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ class CgSpringIntegrationTestClassConstructor(
4040
private val springCodeGenerationContext: SpringCodeGenerationContext,
4141
private val springSettings: PresentSpringSettings,
4242
) : CgAbstractSpringTestClassConstructor(context) {
43+
44+
private val autowiredFieldManager = CgAutowiredFieldsManager(context)
45+
4346
companion object {
4447
private val logger = KotlinLogging.logger {}
4548
}
@@ -52,7 +55,7 @@ class CgSpringIntegrationTestClassConstructor(
5255
override fun constructClassFields(testClassModel: SpringTestClassModel): List<CgFieldDeclaration> {
5356
val autowiredFromContextModels =
5457
testClassModel.springSpecificInformation.autowiredFromContextModels
55-
return constructFieldsWithAnnotation(autowiredClassId, autowiredFromContextModels)
58+
return constructFieldsWithAnnotation(autowiredFieldManager, autowiredFromContextModels)
5659
}
5760

5861
override fun constructAdditionalTestMethods() =

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringUnitTestClassConstructor.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,19 @@ import org.utbot.framework.plugin.api.util.objectClassId
2222
class CgSpringUnitTestClassConstructor(context: CgContext) : CgAbstractSpringTestClassConstructor(context) {
2323

2424
private var additionalMethodsRequired: Boolean = false
25-
2625
private lateinit var mockitoCloseableVariable: CgValue
2726

27+
private val injectingMocksFieldsManager = CgInjectingMocksFieldsManager(context)
28+
private val mocksFieldsManager = CgMockedFieldsManager(context)
29+
2830
override fun constructClassFields(testClassModel: SpringTestClassModel): List<CgFieldDeclaration> {
2931
val fields = mutableListOf<CgFieldDeclaration>()
3032
val thisInstances = testClassModel.springSpecificInformation.thisInstanceModels
3133
val mocks = testClassModel.springSpecificInformation.thisInstanceDependentMocks
3234

3335
if (mocks.isNotEmpty()) {
34-
val mockedFields = constructFieldsWithAnnotation(mockClassId, mocks)
35-
val injectingMocksFields = constructFieldsWithAnnotation(injectMocksClassId, thisInstances)
36+
val mockedFields = constructFieldsWithAnnotation(mocksFieldsManager, mocks)
37+
val injectingMocksFields = constructFieldsWithAnnotation(injectingMocksFieldsManager, thisInstances)
3638

3739
fields += injectingMocksFields
3840
fields += mockedFields

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import org.utbot.framework.plugin.api.util.stringClassId
1212
class CgSpringVariableConstructor(context: CgContext) : CgVariableConstructor(context) {
1313
val annotatedModelGroups: MutableMap<ClassId, MutableSet<UtModelWrapper>> = mutableMapOf()
1414

15-
private val classFieldManager = ClassFieldManagerFacade(context)
15+
private val fieldManagerFacade = ClassFieldManagerFacade(context)
1616

1717
override fun getOrCreateVariable(model: UtModel, name: String?): CgValue {
18-
val variable = classFieldManager.constructVariableForField(model, annotatedModelGroups)
18+
val variable = fieldManagerFacade.constructVariableForField(model)
1919

2020
variable?.let { return it }
2121

0 commit comments

Comments
 (0)