add tests

This commit is contained in:
Denis Savosin
2024-10-02 12:03:20 +07:00
parent cbf7e0a5f6
commit b89ee800ed
5 changed files with 189 additions and 63 deletions

View File

@@ -5,6 +5,7 @@ import com.example.demo.services.kafka.ProducerImpl
import com.example.demo.services.kafka.dto.serializer.ProductSerializer
import org.apache.kafka.clients.producer.ProducerConfig
import org.apache.kafka.common.serialization.StringSerializer
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@@ -34,7 +35,7 @@ class ProducerConfig(
)
@Bean
fun producer(): Producer = ProducerImpl(
kafkaTemplate(),
fun producer(@Autowired kafkaTemplate: KafkaTemplate<String, Any>): Producer = ProducerImpl(
kafkaTemplate,
)
}

View File

@@ -15,5 +15,5 @@ import org.testcontainers.junit.jupiter.Testcontainers
@EnableJdbcRepositories
class BaseFeatureTest {
@MockBean
private lateinit var producer: Producer
lateinit var producer: Producer
}

View File

@@ -7,7 +7,6 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.hamcrest.Matchers.contains
import org.hamcrest.Matchers.nullValue
import org.junit.jupiter.api.Test
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.verifyNoInteractions
import org.mockito.kotlin.whenever
@@ -48,7 +47,8 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc) {
whenever(productService.findByGuid(
eq(guid),
)) doReturn product
))
.thenReturn(product)
mockMvc.get("/api/product/$guid")
.andExpect { status { status { isOk() } } }
@@ -66,7 +66,8 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc) {
whenever(productService.findByGuid(
eq(guid),
)) doReturn null
))
.thenReturn(null)
mockMvc.get("/api/product/$guid")
.andExpect { status { status { isNotFound() } } }
@@ -89,16 +90,17 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc) {
eq(name),
eq(price),
eq(description)
)) doReturn Product(
id = productId,
guid = UUID.randomUUID(),
name = name,
description = description,
price = price,
createdAt = OffsetDateTime.now(),
updatedAt = null,
deletedAt = null,
)
))
.thenReturn(Product(
id = productId,
guid = UUID.randomUUID(),
name = name,
description = description,
price = price,
createdAt = OffsetDateTime.now(),
updatedAt = null,
deletedAt = null,
))
mockMvc.post("/api/product") {
contentType = MediaType.APPLICATION_JSON
@@ -118,8 +120,6 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc) {
mapOf("description" to description, "price" to price)
)
verifyNoInteractions(productService)
mockMvc.post("/api/product") {
contentType = MediaType.APPLICATION_JSON
content = reqBody
@@ -128,6 +128,8 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc) {
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
.andExpect { jsonPath("\$.status") { value(ResponseStatus.BAD_REQUEST.status) } }
.andExpect { jsonPath("\$.cause") { contains("name") } }
verifyNoInteractions(productService)
}
@Test
@@ -139,8 +141,6 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc) {
mapOf("name" to "", "description" to description, "price" to price)
)
verifyNoInteractions(productService)
mockMvc.post("/api/product") {
contentType = MediaType.APPLICATION_JSON
content = reqBody
@@ -149,6 +149,8 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc) {
.andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
.andExpect { jsonPath("\$.status") { value(ResponseStatus.UNPROCESSABLE.status) } }
.andExpect { jsonPath("\$.cause") { value(MethodArgumentNotValidException::class.qualifiedName) } }
verifyNoInteractions(productService)
}
@Test
@@ -157,16 +159,17 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc) {
whenever(productService.delete(
eq(guid),
)) doReturn Product(
id = 2133,
guid = guid,
name = "name",
description = "description",
price = 210202,
createdAt = OffsetDateTime.now(),
updatedAt = null,
deletedAt = OffsetDateTime.now(),
)
))
.thenReturn(Product(
id = 2133,
guid = guid,
name = "name",
description = "description",
price = 210202,
createdAt = OffsetDateTime.now(),
updatedAt = null,
deletedAt = OffsetDateTime.now(),
))
mockMvc.delete("/api/product/${guid}")
.andExpect { status { status { isOk() } } }

View File

@@ -0,0 +1,69 @@
package com.example.demo.services
import com.example.demo.BaseFeatureTest
import com.example.demo.exceptions.NotFoundException
import com.example.demo.exceptions.UnprocessableException
import com.example.demo.models.Product
import com.example.demo.provider.ProductRepository
import org.junit.jupiter.api.assertThrows
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.ContextConfiguration
import java.util.*
import kotlin.test.*
@ContextConfiguration(classes = [ProductRepository::class])
class ProductServiceImplFeatureTest: BaseFeatureTest() {
private lateinit var productService: ProductServiceImpl
@Autowired
private lateinit var productRepository: ProductRepository
@BeforeTest
fun setUp() {
productService = ProductServiceImpl(
defaultSyncTopic = "some-default-topic",
productRepository = productRepository,
producer = producer
)
}
@Test
fun createFindDelete_success() {
val name = "new-product-name"
val price = 33333.toLong()
val description = "some-description"
var product: Product? = null
try {
product = productService.create(name = name, price = price, description = description)
assertNotNull(product.id)
assertEquals(name, product.name)
assertEquals(price, product.price)
assertEquals(333.33, product.getPriceDouble())
val dbProduct = productService.findByGuid(product.guid)
assertNotNull(dbProduct)
assertEquals(product.id, dbProduct.id)
assertFalse(dbProduct.isDeleted())
val deletedProduct = productService.delete(product.guid)
assertNotNull(deletedProduct)
assertEquals(product.id, deletedProduct.id)
assertNotNull(deletedProduct.deletedAt)
assertTrue(deletedProduct.isDeleted())
// try to delete already deleted product
assertThrows<UnprocessableException> {
productService.delete(product.guid)
}
assertThrows<NotFoundException> {
productService.delete(UUID.randomUUID())
}
} finally {
val id = product?.id
if (id != null) {
productRepository.deleteById(id)
}
}
}
}

View File

@@ -1,49 +1,102 @@
package com.example.demo.services
import com.example.demo.BaseFeatureTest
import com.example.demo.exceptions.NotFoundException
import com.example.demo.models.Product
import com.example.demo.provider.ProductRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.ContextConfiguration
import kotlin.test.*
import com.example.demo.services.kafka.Producer
import com.example.demo.services.kafka.exceptions.InvalidArgumentException
import org.junit.jupiter.api.assertThrows
import org.junit.runner.RunWith
import org.mockito.kotlin.*
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.test.context.junit4.SpringRunner
import java.time.OffsetDateTime
import java.util.*
import kotlin.test.BeforeTest
import kotlin.test.Test
@ContextConfiguration(classes = [ProductRepository::class, ProductServiceImpl::class])
class ProductServiceImplTest: BaseFeatureTest() {
@Autowired
@RunWith(SpringRunner::class)
@SpringBootTest
class ProductServiceImplTest {
private val defaultTopic = "some-default-topic"
private lateinit var productService: ProductServiceImpl
@Autowired
@MockBean
@Qualifier("producer")
private lateinit var producer: Producer
@MockBean
private lateinit var productRepository: ProductRepository
@BeforeTest
fun setUp() {
productService = ProductServiceImpl(
defaultSyncTopic = defaultTopic,
productRepository = productRepository,
producer = producer,
)
}
@Test
fun createFindDelete_success() {
val name = "new-product-name"
val price = 33333.toLong()
val description = "some-description"
var product: Product? = null
fun syncToKafka_success() {
val guid = UUID.randomUUID()
val product = Product(
id = 123,
guid = guid,
name = "name",
description = "description",
price = 10050,
createdAt = OffsetDateTime.now().minusDays(1),
updatedAt = OffsetDateTime.now().minusHours(2),
deletedAt = OffsetDateTime.now(),
)
try {
product = productService.create(name = name, price = price, description = description)
assertNotNull(product.id)
assertEquals(name, product.name)
assertEquals(price, product.price)
assertEquals(333.33, product.getPriceDouble())
whenever(productRepository.findByGuid(eq(guid)))
.thenReturn(product)
whenever(producer.produceProductInfo(defaultTopic, product)).doAnswer{}
val dbProduct = productService.findByGuid(product.guid)
assertNotNull(dbProduct)
assertEquals(product.id, dbProduct.id)
assertFalse(dbProduct.isDeleted())
productService.syncToKafka(guid, null)
}
val deletedProduct = productService.delete(product.guid)
assertNotNull(deletedProduct)
assertEquals(product.id, deletedProduct.id)
assertNotNull(deletedProduct.deletedAt)
assertTrue(deletedProduct.isDeleted())
} finally {
val id = product?.id
if (id != null) {
productRepository.deleteById(id)
}
@Test
fun syncToKafka_notFound() {
val specificTopic = "specificNotice"
val guid = UUID.randomUUID()
whenever(productRepository.findByGuid(eq(guid)))
.thenReturn(null)
assertThrows<NotFoundException> {
productService.syncToKafka(guid, specificTopic)
}
verifyNoInteractions(producer)
}
@Test
fun syncToKafka_invalidArgumentException() {
val specificTopic = "specificNotice"
val guid = UUID.randomUUID()
val product = Product(
id = 123,
guid = guid,
name = "name",
description = "description",
price = 10050,
createdAt = OffsetDateTime.now().minusDays(1),
updatedAt = OffsetDateTime.now().minusHours(2),
deletedAt = OffsetDateTime.now(),
)
whenever(productRepository.findByGuid(eq(guid)))
.thenReturn(product)
whenever(producer.produceProductInfo(specificTopic, product))
.doThrow(InvalidArgumentException("some error"))
assertThrows< InvalidArgumentException> {
productService.syncToKafka(guid, specificTopic)
}
}
}