fixture-monkey
fixture-monkey copied to clipboard
스프링 시큐리티의 User를 상속받은 코틀린 클래스 생성 오류
Describe the bug
org.springframework.security.core.userdetails.User를 상속받은 코틀린 클래스 생성시 오류가 발생합니다.
스프링 프로젝트와 엮여 있기 때문에 혼자 해보려고 했으나, 에러 로그만으로는 파악하기 어려워 버그 리포트 드립니다.
Your environment
- version of Fixture Monkey
- 1.0.23
- version of Java/Kotlin
- Java 17, Kotlin 2.0.0
Steps to reproduce
gradle
plugins {
kotlin("jvm") version "2.0.0"
kotlin("plugin.spring") version "1.9.24"
id("org.springframework.boot") version "3.3.2"
id("io.spring.dependency-management") version "1.1.6"
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testImplementation("org.springframework.security:spring-security-test")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter-kotlin:1.0.23")
testImplementation(kotlin("test"))
}
kotlin {
compilerOptions {
freeCompilerArgs.addAll("-Xjsr305=strict")
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
code
import com.navercorp.fixturemonkey.FixtureMonkey
import com.navercorp.fixturemonkey.kotlin.KotlinPlugin
import com.navercorp.fixturemonkey.kotlin.giveMeOne
import org.junit.jupiter.api.RepeatedTest
import org.springframework.security.core.authority.AuthorityUtils
import org.springframework.security.core.userdetails.User
class FixtureTest {
@RepeatedTest(10)
fun `User를 상속받은 클래스는 에러를 던진다`() {
val fixture = FixtureMonkey.builder()
.plugin(KotlinPlugin())
.build()
// cannot invoke "java.lang.Integer.intValue()" because the return value of ...
fixture.giveMeOne<ServiceUser>()
}
}
data class ServiceUser(val nickname: String) : User(
nickname,
"password",
AuthorityUtils.createAuthorityList("NORMAL")
)
Expected behaviour
에러 없이 객체 생성
Actual behaviour
높은 확률로 에러 발생
Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.lang.reflect.InvocationHandler.invoke(Object, java.lang.reflect.Method, Object[])" is null
java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.lang.reflect.InvocationHandler.invoke(Object, java.lang.reflect.Method, Object[])" is null
at jdk.proxy3/jdk.proxy3.$Proxy20.hashCode(Unknown Source)
at java.base/java.util.HashMap.hash(HashMap.java:338)
at java.base/java.util.HashMap.put(HashMap.java:610)
at java.base/java.util.HashSet.add(HashSet.java:221)
at com.navercorp.fixturemonkey.api.context.MonkeyGeneratorContext.isUniqueAndCheck(MonkeyGeneratorContext.java:42)
at com.navercorp.fixturemonkey.api.generator.ArbitraryGeneratorContext.isUniqueAndCheck(ArbitraryGeneratorContext.java:137)
at com.navercorp.fixturemonkey.api.introspector.SetIntrospector.lambda$null$0(SetIntrospector.java:66)
at com.navercorp.fixturemonkey.api.arbitrary.FilteredCombinableArbitrary.combined(FilteredCombinableArbitrary.java:74)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
at com.navercorp.fixturemonkey.api.arbitrary.ContainerCombinableArbitrary.combined(ContainerCombinableArbitrary.java:48)
at com.navercorp.fixturemonkey.api.arbitrary.NullInjectCombinableArbitrary.combined(NullInjectCombinableArbitrary.java:46)
at com.navercorp.fixturemonkey.api.arbitrary.TraceableCombinableArbitrary.combined(TraceableCombinableArbitrary.java:42)
at com.navercorp.fixturemonkey.api.arbitrary.ObjectCombinableArbitrary.lambda$combined$0(ObjectCombinableArbitrary.java:51)
at java.base/java.util.HashMap.forEach(HashMap.java:1421)
at com.navercorp.fixturemonkey.api.arbitrary.ObjectCombinableArbitrary.combined(ObjectCombinableArbitrary.java:50)
at com.navercorp.fixturemonkey.api.arbitrary.NullInjectCombinableArbitrary.combined(NullInjectCombinableArbitrary.java:46)
at com.navercorp.fixturemonkey.api.arbitrary.TraceableCombinableArbitrary.combined(TraceableCombinableArbitrary.java:42)
at com.navercorp.fixturemonkey.api.arbitrary.FilteredCombinableArbitrary.combined(FilteredCombinableArbitrary.java:73)
at com.navercorp.fixturemonkey.resolver.ResolvedCombinableArbitrary.combined(ResolvedCombinableArbitrary.java:77)
at com.navercorp.fixturemonkey.resolver.DefaultArbitraryBuilder.sample(DefaultArbitraryBuilder.java:489)
at com.navercorp.fixturemonkey.FixtureMonkey.lambda$giveMe$3(FixtureMonkey.java:158)
at java.base/java.util.stream.StreamSpliterators$InfiniteSupplyingSpliterator$OfRef.tryAdvance(StreamSpliterators.java:1358)
at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129)
at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
at com.navercorp.fixturemonkey.FixtureMonkey.giveMe(FixtureMonkey.java:166)
at com.navercorp.fixturemonkey.FixtureMonkey.giveMeOne(FixtureMonkey.java:174)
at FixtureTest.User를 상속받은 클래스는 에러를 던진다(FixtureTest.kt:26)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.stream.IntPipeline$1$1.accept(IntPipeline.java:180)
at java.base/java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:104)
at java.base/java.util.Spliterator$OfInt.forEachRemaining(Spliterator.java:711)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
@jurogrammer 안녕하세요.
1.0.x에서는 KotlinPlugin에서 제공하는 생성 방식이 자바 타입과 코틀린 타입을 분리하지 않고 모두 호환하고 있습니다.
코틀린에서도 자바에서와 같이 타입의 필드를 모두 생성하려다가 이 같은 이슈가 발생한 것으로 보입니다.
임시로 FixtureMonkeyBuilder에 아래 옵션을 추가하시면 해결될 것으로 보입니다.
.defaultPropertyGenerator(
KotlinPropertyGenerator(
javaDelegatePropertyGenerator = ConstructorPropertiesArbitraryIntrospector.PROPERTY_GENERATOR
)
)
1.1.x 부터는 언어마다 생성 방식을 분리하여 이 같은 버그가 없을 예정입니다.
감사합니다.
정상동작 확인했습니다. 감사합니다!