Issue
I have a small piece of code on Kotlin in which Producer is configured.
testImplementation(kotlin("test"))
testImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0")
- properties
@ConfigurationProperties("initial")
data class KafkaProperties (
@field:NotEmpty
var topic: String = "topic",
@field:NestedConfigurationProperty
val kafka: KafkaProperties = KafkaProperties()
)
- record
class Event (val id: Long) {
}
- producer
@Configuration
@EnableConfigurationProperties(KafkaProperties::class)
class KafkaConfiguration (
private val properties: KafkaProperties
) {
@Bean
fun initialProducerFactory(customizers: ObjectProvider<DefaultKafkaProducerFactoryCustomizer>):
ProducerFactory<String,Event> {
val producerProperties = this.properties.kafka.buildProducerProperties()
val factory =
buildFactory(producerProperties)
customizers.orderedStream()
.forEach {
customizer -> customizer.customize(factory)
}
return factory
}
private fun buildFactory(producerProperties: Map<String, Any>):
DefaultKafkaProducerFactory<String, Event> {
val factory = DefaultKafkaProducerFactory<String, Event>(producerProperties)
factory.keySerializer = StringSerializer()
val objectMapper = JacksonUtils.enhancedObjectMapper().disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
val jsonSerializer = JsonSerializer<Event>(objectMapper)
factory.valueSerializer = jsonSerializer
val transactionIdPrefix = this.properties.kafka.producer.transactionIdPrefix
if (transactionIdPrefix != null) {
factory.setTransactionIdPrefix(transactionIdPrefix)
}
return factory
}
}
- service
@Service
class ServiceImpl(
private val kafkaProperties: KafkaProperties,
private val producerFactory: ProducerFactory<String, Event>
) : Service {
private fun runPublishEventToKafka(Long id){
val eventRequest = Event(id)
val topic = kafkaProperties.topic
val producer = this.producerFactory.createProducer()
producer.send(ProducerRecord(topic, eventRequest))
}
}
I made a small blank in the test class
class ServiceImplTest {
private val kafkaProperties: KafkaProperties = mock()
private val producerFactory: ProducerFactory<String, Event> = mock()
@BeforeEach
fun setUp() {
// val producerFactory =
mock<ProducerFactory<String, Event>>()
// val eventRequest = Event(1L)
// val topicTest = "topic.test"
// val producer = producerFactory.createProducer()
// val send = producer.send(ProducerRecord(topicTest, eventClosureRequest))
whenever(producer.send(any())
).thenReturn(????) //fail
}
@Test
fun test(){
//....
}
Who has any ideas on how to make a stub for calling a method -
producer.send (...)
?
Update
class ServiceImplTest {
private val persistenceService: PersistenceService = mock()
private val kafkaProperties: KafkaProperties = mock()
val user = TestUserFactory().user()
..........
lateinit var sut: ServiceImpl
@BeforeEach
fun setUp() {
val mockProducerFactory = mock<ProducerFactory<String, Event>>()
val mockProducer = mock<Producer<String, Event>>()
sut = ServiceImpl(
PersistenceService,
kafkaProperties,
mockProducerFactory)
whenever(mockProducerFactory.createProducer()).thenReturn(mockProducer)
whenever(persistenceService.loadUserById(any())).thenReturn(Optional.of(user))
}
Here is another example of a class where mock had to be embedded to simulate calling the producer.send(.....) method
@DataJpaTest
@ActiveProfiles("test")
@ContextConfiguration(
initializers = [RedisInit::class, DBContainerInit::class],
classes = [ServiceImpl::class, DataSourceConfiguration::class]
)
@Import(value = [CacheConfiguration::class])
class ServiceCacheTest {
@MockBean
private lateinit var persistenceService: PersistenceService
@Suppress("unused")
@MockBean
private lateinit var rService: RService
@Suppress("unused")
@MockBean
private lateinit var kafkaProperties: KafkaProperties
@Suppress("unused")
@MockBean
private lateinit var producerFactory: ProducerFactory<String, Event>
@Autowired
private lateinit var service: UserService
@Autowired
private lateinit var redisTemplate: StringRedisTemplate
@BeforeEach
fun setUp() {
val mockProducer =
mock<Producer<String, Event>>()
whenever(producerFactory.createProducer()).thenReturn(mockProducer)
}
@Test
fun `should call......
..........
Based on the answer provided below, I was able to get the expected behavior from the test.
Solution
You need to use a chain of mocks if you want that stubbed because you use a chain of ProducerFactory
-> Producer
-> send
. Both the ProducerFactory
and Producer
need to be mocked. One problem in your setup is you are calling producerFactory.createProducer()
on a mocked producerFactory
which should be returning null
by default.
This is how I would set it up
val producerFactory = mock<ProducerFactory<String, Event>>()
val mockProducer = mock<Producer<String, Event>()
whenever(mockProducerFactory.createProducer()).thenReturn(mockProducer)
You shouldn't be calling producerFactory.createProducer()
nor producer.send
directly in your mock setups.
You didn't specify the return type of send
so I'm leaving that blank, but the above should be valid if doesn't return anything. If you want to verify it was called then all you do is
verify(mockProducer).send(any())
Be forewarned that you didn't provide a lot of test code and you're still responsible for how the mock ProducerFactory
gets injected into the production code you're testing.
Answered By - possum
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.