mirror of
https://github.com/Dannecron/spring-boot-demo.git
synced 2025-12-25 16:22:35 +03:00
add tests
This commit is contained in:
@@ -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,
|
||||
)
|
||||
}
|
||||
@@ -15,5 +15,5 @@ import org.testcontainers.junit.jupiter.Testcontainers
|
||||
@EnableJdbcRepositories
|
||||
class BaseFeatureTest {
|
||||
@MockBean
|
||||
private lateinit var producer: Producer
|
||||
lateinit var producer: Producer
|
||||
}
|
||||
@@ -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,7 +90,8 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc) {
|
||||
eq(name),
|
||||
eq(price),
|
||||
eq(description)
|
||||
)) doReturn Product(
|
||||
))
|
||||
.thenReturn(Product(
|
||||
id = productId,
|
||||
guid = UUID.randomUUID(),
|
||||
name = name,
|
||||
@@ -98,7 +100,7 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc) {
|
||||
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,7 +159,8 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc) {
|
||||
|
||||
whenever(productService.delete(
|
||||
eq(guid),
|
||||
)) doReturn Product(
|
||||
))
|
||||
.thenReturn(Product(
|
||||
id = 2133,
|
||||
guid = guid,
|
||||
name = "name",
|
||||
@@ -166,7 +169,7 @@ class ProductControllerTest(@Autowired val mockMvc: MockMvc) {
|
||||
createdAt = OffsetDateTime.now(),
|
||||
updatedAt = null,
|
||||
deletedAt = OffsetDateTime.now(),
|
||||
)
|
||||
))
|
||||
|
||||
mockMvc.delete("/api/product/${guid}")
|
||||
.andExpect { status { status { isOk() } } }
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
|
||||
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)
|
||||
productService.syncToKafka(guid, null)
|
||||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user