Skip to content

Commit 14fc1ea

Browse files
Avoid spying collection-like types with @SPY if it may mislead @Injectmocks behaviour (#2642)
* Fix bug with injecting of different types of thisInstance fields using @SPY. --------- Co-authored-by: Egor Kulikov <egor.k.kulikov@gmail.com>
1 parent 09aebe1 commit 14fc1ea

File tree

2 files changed

+33
-8
lines changed
  • utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api
  • utbot-spring-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager

2 files changed

+33
-8
lines changed

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -404,13 +404,10 @@ fun UtModel.isMockModel() = this is UtCompositeModel && isMock
404404
* Checks that this [UtModel] must be constructed with @Spy annotation in generated tests.
405405
* Used only for construct variables with @Spy annotation.
406406
*/
407-
fun UtModel.canBeSpied(): Boolean {
408-
val javaClass = this.classId.jClass
407+
fun UtModel.canBeSpied(): Boolean =
408+
this is UtAssembleModel && spiedTypes.any { type -> type.isAssignableFrom(this.classId.jClass)}
409409

410-
return this is UtAssembleModel &&
411-
(Collection::class.java.isAssignableFrom(javaClass)
412-
|| Map::class.java.isAssignableFrom(javaClass))
413-
}
410+
val spiedTypes = setOf(Collection::class.java, Map::class.java)
414411

415412
/**
416413
* Get model id (symbolic null value for UtNullModel)

utbot-spring-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgSpiedFieldsManager.kt

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.utbot.framework.codegen.tree.fieldmanager
22

3+
import org.utbot.framework.codegen.domain.UtModelWrapper
34
import org.utbot.framework.codegen.domain.builtin.spyClassId
45
import org.utbot.framework.codegen.domain.context.CgContext
56
import org.utbot.framework.codegen.domain.models.CgFieldDeclaration
@@ -12,6 +13,8 @@ import org.utbot.framework.plugin.api.UtAssembleModel
1213
import org.utbot.framework.plugin.api.UtModel
1314
import org.utbot.framework.plugin.api.canBeSpied
1415
import org.utbot.framework.plugin.api.isMockModel
16+
import org.utbot.framework.plugin.api.spiedTypes
17+
import org.utbot.framework.plugin.api.util.jClass
1518

1619
class CgSpiedFieldsManager(context: CgContext) : CgAbstractClassFieldManager(context) {
1720

@@ -38,10 +41,10 @@ class CgSpiedFieldsManager(context: CgContext) : CgAbstractClassFieldManager(con
3841
cgModel !in dependentMockModels
3942
}
4043

41-
return constructFieldsWithAnnotation(dependentSpyModels)
44+
val suitableSpyModels = getSuitableSpyModels(dependentSpyModels)
45+
return constructFieldsWithAnnotation(suitableSpyModels)
4246
}
4347

44-
4548
private val spyFrameworkManager = SpyFrameworkManager(context)
4649

4750
override fun useVariableForModel(model: UtModel, variable: CgValue) {
@@ -57,4 +60,29 @@ class CgSpiedFieldsManager(context: CgContext) : CgAbstractClassFieldManager(con
5760
classId.canBeInjectedByTypeInto(classUnderTest)
5861

5962
override fun constructBaseVarName(model: UtModel): String = super.constructBaseVarName(model) + "Spy"
63+
64+
private fun getSuitableSpyModels(potentialSpyModels: MutableSet<UtModelWrapper>): Set<UtModelWrapper> =
65+
spiedTypes.fold(setOf()) { spyModels, type ->
66+
spyModels + getSuitableSpyModelsOfType(type, potentialSpyModels)
67+
}
68+
69+
/*
70+
* Filters out cases when different tests use different [clazz]
71+
* implementations and hence we need to inject different types.
72+
*
73+
* This limitation is reasoned by @InjectMocks behaviour.
74+
* Otherwise, injection may be misleading:
75+
* for example, several spies may be injected into one field.
76+
*/
77+
private fun getSuitableSpyModelsOfType(
78+
clazz: Class<*>,
79+
potentialSpyModels: MutableSet<UtModelWrapper>
80+
): Set<UtModelWrapper> {
81+
val spyModelsAssignableFrom = potentialSpyModels
82+
.filter { clazz.isAssignableFrom(it.model.classId.jClass) }
83+
.toSet()
84+
val spyModelsTypesCount = spyModelsAssignableFrom.map { it.model.classId }.toSet().size
85+
86+
return if (spyModelsTypesCount == 1) spyModelsAssignableFrom else emptySet()
87+
}
6088
}

0 commit comments

Comments
 (0)