Skip to content

Avoid spying collection-like types with @Spy if it may mislead @InjectMocks behaviour #2642

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -404,13 +404,10 @@ fun UtModel.isMockModel() = this is UtCompositeModel && isMock
* Checks that this [UtModel] must be constructed with @Spy annotation in generated tests.
* Used only for construct variables with @Spy annotation.
*/
fun UtModel.canBeSpied(): Boolean {
val javaClass = this.classId.jClass
fun UtModel.canBeSpied(): Boolean =
this is UtAssembleModel && spiedTypes.any { type -> type.isAssignableFrom(this.classId.jClass)}

return this is UtAssembleModel &&
(Collection::class.java.isAssignableFrom(javaClass)
|| Map::class.java.isAssignableFrom(javaClass))
}
val spiedTypes = setOf(Collection::class.java, Map::class.java)

/**
* Get model id (symbolic null value for UtNullModel)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.utbot.framework.codegen.tree.fieldmanager

import org.utbot.framework.codegen.domain.UtModelWrapper
import org.utbot.framework.codegen.domain.builtin.spyClassId
import org.utbot.framework.codegen.domain.context.CgContext
import org.utbot.framework.codegen.domain.models.CgFieldDeclaration
Expand All @@ -12,6 +13,8 @@ import org.utbot.framework.plugin.api.UtAssembleModel
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.canBeSpied
import org.utbot.framework.plugin.api.isMockModel
import org.utbot.framework.plugin.api.spiedTypes
import org.utbot.framework.plugin.api.util.jClass

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

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

return constructFieldsWithAnnotation(dependentSpyModels)
val suitableSpyModels = getSuitableSpyModels(dependentSpyModels)
return constructFieldsWithAnnotation(suitableSpyModels)
}


private val spyFrameworkManager = SpyFrameworkManager(context)

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

override fun constructBaseVarName(model: UtModel): String = super.constructBaseVarName(model) + "Spy"

private fun getSuitableSpyModels(potentialSpyModels: MutableSet<UtModelWrapper>): Set<UtModelWrapper> =
spiedTypes.fold(setOf()) { spyModels, type ->
spyModels + getSuitableSpyModelsOfType(type, potentialSpyModels)
}

/*
* Filters out cases when different tests use different [clazz]
* implementations and hence we need to inject different types.
*
* This limitation is reasoned by @InjectMocks behaviour.
* Otherwise, injection may be misleading:
* for example, several spies may be injected into one field.
*/
private fun getSuitableSpyModelsOfType(
clazz: Class<*>,
potentialSpyModels: MutableSet<UtModelWrapper>
): Set<UtModelWrapper> {
val spyModelsAssignableFrom = potentialSpyModels
.filter { clazz.isAssignableFrom(it.model.classId.jClass) }
.toSet()
val spyModelsTypesCount = spyModelsAssignableFrom.map { it.model.classId }.toSet().size

return if (spyModelsTypesCount == 1) spyModelsAssignableFrom else emptySet()
}
}