Kotlin sealed class Jackson different place in memory - spring

Recently, I have started with Kotlin and I encountered some strange behavior while testing JSON mapping with Spring.
I created something like this:
#SpringBootTest(classes = [TestApplication::class])
class JacksonIntegrationTest {
#Autowired
lateinit var objectMapper: ObjectMapper
var objectMapperTest = TestObjectMapper()
#Test
fun `should serialize and deserialize object`() {
//given
val value = SealedObject
//when
val jsonTest = objectMapperTest.writeValueAsString(value)
val resultTest: SealedObject = objectMapperTest.readValue(jsonTest)
val json = objectMapper.writeValueAsString(value)
val result: SealedObject = objectMapper.readValue(json)
//then`
assertThat(result).isSameAs(value)
assertThat(resultTest).isSameAs(value) <---------- FAILED
}
internal sealed class Sealed
internal object SealedObject: Sealed()
}
value = JacksonIntegrationTest$SealedObject#6727e0cd <-------------\
result (SPRING) = JacksonIntegrationTest$SealedObject#6727e0cd <----- SAME MEMORY PLACE
resultTest (OWN) = JacksonIntegrationTest$SealedObject#3c8e3f98
As you can see, spring objectmapper returned value with same reference at memory as origin value.
But own created ObjectMapper returned object at different place at memory. Why?
All results should've same memory place

Okay, I've found a solution.
.registerModule(
kotlinModule(
initializer = {
configure(KotlinFeature.SingletonSupport, true)
},
),
)

Related

How to wire #Configuration files from Unit Test - Spring Boot + Kotlin

I have an application.yml with some configuration properties required by my application.
SF:
baseurl: https://xxxxx
case:
recordTypeId: 0124a0000004Ifb
application:
recordTypeId: 0125P000000MkDa
address:
personal:
recordTypeId: 0125P000000MnuO
business:
recordTypeId: 0125P000000MnuT
I have defined a configuration class to read those properties as follows:
#Configuration
class SFProperties(
#Value("\${sf.case.recordTypeId}") val caseRecordTypeId: String,
#Value("\${sf.application.recordTypeId}") val applicationRecordTypeId: String,
#Value("\${sf.address.personal.recordTypeId}") val addressPersonalRecordTypeId:String,
#Value("\${sf.address.business.recordTypeId}") val addressBusinessRecordTypeId: String
)
The class is wired within a service without any issues,
#Service
class SFClientManagementServiceImpl( val webClientBuilder: WebClient.Builder):
ClientManagementService {
....
#Autowired
lateinit var sfProperties: SFProperties
override fun createCase(caseRequest: CaseRequestDto): Mono<CaseResponseDto> {
...
var myValue= sfProperties.caseRecordTypeId
....
}
}
When trying to test this service, I get a "lateinit property sfProperties has not been initialized" exception:
The test looks as follows:
#SpringBootTest(classes = [SFProperties::class])
class SalesforceClientManagementServiceImplTests {
#Autowired
open lateinit var sfProperties: SFProperties
#Test
fun `createCase should return case id when case is created`() {
val clientResponse: ClientResponse = ClientResponse
.create(HttpStatus.OK)
.header("Content-Type", "application/json")
.body(ObjectMapper().writeValueAsString(Fakes().GetFakeCaseResponseDto())).build()
val shortCircuitingExchangeFunction = ExchangeFunction {
Mono.just(clientResponse)
}
val webClientBuilder: WebClient.Builder = WebClient.builder().exchangeFunction(shortCircuitingExchangeFunction)
val sfClientManagementServiceImpl =
SFClientManagementServiceImpl(webClientBuilder)
var caseResponseDto =
salesforceClientManagementServiceImpl.createCase(Fakes().GetFakeCaseRequestDto())
var response = caseResponseDto.block()
if (response != null) {
assertEquals(Fakes().GetFakeCaseResponseDto().id, response.id)
}
}
I have tried many other annotations on the Test class but without success, I would appreciate any ideas.

Null when injecting mock services, testing a RestController - Mockito

I am testing a REST controller, and I'd like to inject mock service.
But I am getting a null value when calling service Mock
this is my code:
Interface:
interface CaseManagementService {
fun createAccount(caseRequest: CaseRequestDto): Mono<CaseResponseDto>
}
Service:
#Service
class CaseManagementServiceImpl(private val clientManagementService:
ClientManagementService) : CaseManagementService {
override fun createAccount(caseRequest: CaseRequestDto): Mono<CaseResponseDto> {
return clientManagementService.createAccount(caseRequest)
}
}
Controller:
#RestController
#RequestMapping("somepath")
class CaseController(private val caseManagementService: CaseManagementService) {
#PostMapping()
fun createCase(#RequestBody caseRequest: CaseRequestDto): Mono<CaseResponseDto> {
return caseManagementService.createAccount(caseRequest) }
}
The test:
#SpringBootTest
class CaseControllerTests {
#Test
fun `createCase should return case id when a case is created`() {
val caseManagementService: CaseManagementServiceImpl =
Mockito.mock(CaseManagementServiceImpl::class.java)
val caseResponseDtoMono = Mono.just(Fakes().GetFakeCaseResponseDto())
val requestDto = Fakes().GetFakeCaseRequestDto()
`when`(caseManagementService.createAccount(requestDto).thenReturn(caseResponseDtoMono))
var caseController = CaseController(caseManagementService)
//NULL EXCEPTION HAPPENS HERE - RETURNS NULL THIS CALL
var result = caseController.createCase(Fakes().GetFakeCaseRequestDto())
StepVerifier.create(result)
.consumeNextWith { r -> assertEquals(Fakes().GetFakeCaseResponseDto().id, r.id)
}.verifyComplete()
}
}
The closing bracket is in a wrong place: you are calling Mono.thenReturn (on a null Mono instance returned from createAccount) instead of the Mockito's thenReturn (I assume that's what you meant):
`when`(caseManagementService.createAccount(requestDto)).thenReturn(caseResponseDtoMono)
Second problem: you are mocking createAccount call for a specific instance of the CaseRequestDto. In the actual call you are using different instance, so the arguments do not match and the mock returns null. Try reusing the request instance, i.e.:
var result = caseController.createCase(requestDto)
You have mocked the service but not injected the mocked service in the rest controller. That's why you are getting a null pointer. So, caseManagementService needs to be injected in CaseController. Below is a link where you can see the injection part. In the below code I have moved caseController variable above so that caseManagementService is injected in caseControler before it is used.
#SpringBootTest
class CaseControllerTests {
#Test
fun `createCase should return case id when a case is created`() {
val caseManagementService: CaseManagementServiceImpl =
Mockito.mock(CaseManagementServiceImpl::class.java)
var caseController = CaseController(caseManagementService)
val caseResponseDtoMono = Mono.just(Fakes().GetFakeCaseResponseDto())
val requestDto = Fakes().GetFakeCaseRequestDto()
`when`(caseManagementService.createAccount(requestDto).thenReturn(caseResponseDtoMono))
//NULL EXCEPTION HAPPENS HERE - RETURNS NULL THIS CALL
var result = caseController.createCase(Fakes().GetFakeCaseRequestDto())
StepVerifier.create(result)
.consumeNextWith { r -> assertEquals(Fakes().GetFakeCaseResponseDto().id, r.id)
}.verifyComplete()
}
}
https://vmaks.github.io/other/2019/11/04/spring-boot-with-mockito-and-kotlin.html

Is there anyway to update #Bean at runtime?

For my project I want to download from an API and store this information in a map. Furthermore I want to have the map as a bean in another class. I suspect the API to update regularly so I have set a #Schedule for downloading the XML file from the API.
To the problem... How can I update the map with the information from the API every time the XML is downloaded. I do not want to reboot the application each time.
I am very new to the Spring framework so if there is a more elegant method to do this please let me know.
data class DataContainer(val dictionary: MutableMap<String, String>)
#Configuration
#Component
class DownloadRenhold {
var dict: MutableMap<String, String> = xmlToDict("/renhold.xml")
val dataContainer: DataContainer
#Bean
get() = DataContainer(dict)
fun download(link: String, path: String) {
URL(link).openStream().use { input ->
FileOutputStream(File(path)).use { output ->
input.copyTo(output)
}
}
}
#Scheduled(fixedRate = 5000)
fun scheduledDL() {
download("www.link.com","src/main/resources/renhold.xml")
dict = xmlToDict("/renhold.xml")
}
class Controller {
#GetMapping(value = ["/{orgnummer}"]) // #RequestMapping(value="/",method=RequestMethod.GET)
fun orgNrRequest(#PathVariable("orgnummer") nr: String): String? {
var actx = AnnotationConfigApplicationContext(DownloadRenhold::class.java)
var dataContainer = actx.getBean(DataContainer::class.java)
return dataContainer.dictionary[nr]
}
```
I would suggest to not have DataContainer as a bean directly. Instead inject DownRenhold into Controller as a singleton bean. Something along these lines:
// No need to make this class a Configuration. Plain Component would suffice.
// #Configuration
#Component
class DownloadRenhold {
var _dataContainer: DataContainer = null
var dataContainer: DataContainer
get() = _dataContainer
#Scheduled(fixedRate = 5000)
fun scheduledDL() {
_dataContainer = // do your download thing and create a DataContainer instance.
}
}
class Controller {
#Autowired
var dataProvider: DownloadRenhold
#GetMapping(value = ["/{orgnummer}"])
#RequestMapping(value="/",method=RequestMethod.GET)
fun orgNrRequest(#PathVariable("orgnummer") nr: String): String? {
dataProvider.dataContainer // access the current data container
}

ClassCastException in Spring Redis Cache

I'm developing a Spring Boot application with Spring Boot version 2.1.8.RELEASE.
I need to build custom RedisCacheManager.
RedisCacheManager is as follows.
#EnableCaching
#Configuration
class CacheConfig {
#Bean
fun redisCacheManager(lettuceConnectionFactory: RedisConnectionFactory): RedisCacheManager? {
val redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1))
return RedisCacheManager.RedisCacheManagerBuilder
.fromConnectionFactory(lettuceConnectionFactory)
.cacheDefaults(redisCacheConfiguration)
.build()
}
}
In my service, I cache response with #Cacheble. See:
#Cacheable(cacheNames = ["cached_sample"])
fun getAllSample(): List<SampleRecord> {
return auditableRepository.findAll()
}
Model I cached:
data class SampleRecord(
#ApiModelProperty(readOnly = true)
val id: Long? = null,
#ApiModelProperty(readOnly = true)
val active: Boolean? = null,
#ApiModelProperty(readOnly = true)
val createdDate: Instant? = null,
val param: String
): Serializable
When I call function second time, I get following exception
Caused by: java.lang.ClassCastException: com.cryptocurrency.exchange.sample.model.SampleRecord cannot be cast to com.cryptocurrency.exchange.sample.model.SampleRecord
What whould be the reason of this exception?
This issue occurs if you Spring dev-tools in your dependency tree. There is a fairly easy solution, but it isn't documented well. You need to set the Redis cache config to reference the Context classloader when deserializing objects. For your code, it would look like:
redisCacheConfiguration = RedisCacheConfiguration
.defaultCacheConfig(Thread.currentThread().getContextClassLoader())
.entryTtl(Duration.ofHours(1))
The .defaultCacheConfig(Thread.currentThread().getContextClassLoader()) makes sure that Redis has a reference to the Context classloader when deserializing. Spring outlines this in the Known Issues sections here: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-devtools-known-restart-limitations

#Transactional in Spring Boot - I believe prerequisites are covered (public, external invocation), but testing indicates no transaction

I'm trying to get a Kotlin function to operate transactionally in Spring Boot, and I've looked at several sources for information, such as https://codete.com/blog/5-common-spring-transactional-pitfalls/ and Spring #Transaction method call by the method within the same class, does not work?. I believe I have the prerequisites necessary for the #Transactional annotation to work - the function is public and being invoked externally, if my understanding is correct. My code currently looks like this:
interface CreateExerciseInstance {
operator fun invoke(input: CreateExerciseInstanceInput): OpOutcome<CreateExerciseInstanceOutput>
}
#Component
class CreateExerciseInstanceImpl constructor(
private val exerciseInstanceRepository: ExerciseInstanceRepository, // #Repository
private val activityInstanceRepository: ActivityInstanceRepository, // #Repository
private val exerciseInstanceStepRepository: ExerciseInstanceStepRepository // #Repository
) : CreateExerciseInstance {
#Suppress("TooGenericExceptionCaught")
#Transactional
override fun invoke(input: CreateExerciseInstanceInput): OpOutcome<CreateExerciseInstanceOutput> {
...
val exerciseInstanceRecord = ... // no in-place modification of repository data
val activityInstanceRecords = ...
val exerciseInstanceStepRecords = ...
return try {
exerciseInstanceRepository.save(exerciseInstanceRecord)
activityInstanceRepository.saveAll(activityInstanceRecords)
exerciseInstanceStepRepository.saveAll(exerciseInstanceStepRecords)
Outcome.Success(...)
} catch (e: Exception) {
Outcome.Failure(...)
}
}
}
My test currently looks like this:
#ExtendWith(SpringExtension::class)
#SpringBootTest
#Transactional
class CreateExerciseInstanceTest {
#Autowired
private lateinit var exerciseInstanceRepository: ExerciseInstanceRepository
#Autowired
private lateinit var exerciseInstanceStepRepository: ExerciseInstanceStepRepository
#Autowired
private lateinit var activityInstanceRepository: ActivityInstanceRepository
#Test
fun `does not commit to exercise instance or activity repositories when exercise instance step repository throws exception`() {
... // data setup
val exerciseInstanceStepRepository = mockk<ExerciseInstanceStepRepository>()
val exception = Exception("Something went wrong")
every { exerciseInstanceStepRepository.save(any<ExerciseInstanceStepRecord>()) } throws exception
val createExerciseInstance = CreateExerciseInstanceImpl(
exerciseInstanceRepository = exerciseInstanceRepository,
activityInstanceRepository = activityInstanceRepository,
exerciseInstanceStepRepository = exerciseInstanceStepRepository
)
val outcome = createExerciseInstance(...)
assert(outcome is Outcome.Failure)
val exerciseInstances = exerciseInstanceRepository.findAll()
val activityInstances = activityInstanceRepository.findAll()
assertThat(exerciseInstances.count()).isEqualTo(0)
assertThat(activityInstances.count()).isEqualTo(0)
}
}
The test fails with:
org.opentest4j.AssertionFailedError:
Expecting:
<1>
to be equal to:
<0>
but was not.
at assertThat(exerciseInstances.count()).isEqualTo(0). Is the function actually non-public or being invoked internally? Have I missed some other prerequisite?
This test doesn't say anything about your component not being transactional.
First, you create an instance yourself rather than using the one created by Spring. So Spring knows nothing about this instance, and can't possibly warp it into a transactional proxy.
Second, the component doesn't throw any runtime exception, So Spring doesn't rollback the transaction.

Resources