Skip to content

Commit

Permalink
Add better test coverage for backend
Browse files Browse the repository at this point in the history
  • Loading branch information
Starefossen committed Nov 2, 2023
1 parent acf9838 commit 3fc0493
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 39 deletions.
1 change: 1 addition & 0 deletions shop-backend/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ dependencies {
runtimeOnly("com.h2database:h2")
runtimeOnly("org.postgresql:postgresql")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.mockito:mockito-core")
}

tasks.withType<KotlinCompile> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,13 @@ class ProductController {
}

@GetMapping("/{id:\\d+}/ratings")
fun findRatingsByProductId(@PathVariable id: Long): ResponseEntity<MutableList<Rating>> {
val product = productRepository.findById(id)
return product.map { ResponseEntity.ok(it.ratings) }.orElse(ResponseEntity.notFound().build())
fun findRatingsByProductId(@PathVariable id: Long): ResponseEntity<List<Rating>> {
val ratings = ratingRepository.findByProductId(id)
return if (ratings.isNotEmpty()) {
ResponseEntity.ok(ratings)
} else {
ResponseEntity.notFound().build()
}
}

@PostMapping("/{id:\\d+}/ratings")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package no.nav.shopbackend.model

import com.fasterxml.jackson.annotation.JsonIgnore
import jakarta.persistence.Entity
import jakarta.persistence.FetchType
import jakarta.persistence.GeneratedValue
Expand All @@ -14,7 +15,10 @@ data class Rating(
@field:NotNull val stars: Int,
val comment: String?,
val sentiment: String?,
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "product_id") val product: Product?,
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_id")
val product: Product?,
@Id @GeneratedValue(strategy = GenerationType.AUTO) val id: Long = -1
) {
private constructor() : this(-1, "", "", null, -1L)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ package no.nav.shopbackend.repo

import no.nav.shopbackend.model.Rating
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.CrudRepository
import org.springframework.data.repository.query.Param
import org.springframework.stereotype.Repository

@Repository
interface RatingRepository : CrudRepository<Rating, Long> {
fun findAll(pageable: Pageable): Iterable<Rating>

@Query("SELECT r FROM Rating r WHERE r.product.id = :productId")
fun findByProductId(@Param("productId") productId: Long): List<Rating>
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
package no.nav.shopbackend.controller

import com.fasterxml.jackson.databind.ObjectMapper
import java.util.Optional
import no.nav.shopbackend.model.Product
import no.nav.shopbackend.model.Rating
import no.nav.shopbackend.repo.ProductRepository
import no.nav.shopbackend.repo.RatingRepository
import no.nav.shopbackend.service.SentimentService
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mockito.times
import org.mockito.ArgumentCaptor
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.PageRequest
import org.springframework.http.MediaType
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.test.web.servlet.MockMvc
Expand All @@ -29,13 +30,17 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
class ProductControllerTest {
@Autowired private lateinit var mockMvc: MockMvc

@MockBean private lateinit var productRepo: ProductRepository
@MockBean private lateinit var ratingRepo: RatingRepository
@MockBean private lateinit var sentimentService: SentimentService

@Test
fun testGetProductById() {
val product =
@MockBean private lateinit var productRepository: ProductRepository

@MockBean private lateinit var ratingRepository: RatingRepository

private lateinit var product: Product

@BeforeEach
fun setUp() {
product =
Product(
"Test Product",
"A test product",
Expand All @@ -46,13 +51,38 @@ class ProductControllerTest {
1L
)

product.ratings.add(Rating(5, "Great product", "positive", product))
product.ratings.add(Rating(1, "Bad product", "negative", product))
product.ratings.add(Rating(3, "Average product", "neutral", product))
product.ratings.add(Rating(4, "Good product", "neutral", product))
val ratings =
mutableListOf<Rating>(
Rating(5, "Great product", "positive", product, 1L),
Rating(1, "Bad product", "negative", product, 2L),
Rating(3, "Average product", "neutral", product, 3L),
Rating(4, "Good product", "neutral", product, 4L)
)

for (rating in ratings) {
product.ratings.add(rating)
}

`when`(productRepo.findById(anyLong())).thenReturn(Optional.ofNullable(product))
`when`(productRepository.findAll(PageRequest.of(0, 10))).thenReturn(PageImpl(listOf(product)))
`when`(productRepository.findById(1L)).thenReturn(java.util.Optional.of(product))
`when`(ratingRepository.findByProductId(1L)).thenReturn(ratings)
}

@Test
fun testGetProducts() {
val result =
mockMvc
.perform(get("/api/products").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk)
.andReturn()

val expected =
"""{"content":[{"name":"Test Product","description":"A test product","category":"TEE_SHIRT","price":13.37,"images":[],"id":1,"averageRating":3.25}],"pageable":"INSTANCE","totalPages":1,"totalElements":1,"last":true,"numberOfElements":1,"first":true,"size":1,"number":0,"sort":{"sorted":false,"unsorted":true,"empty":true},"empty":false}"""
assertEquals(expected, result.response.contentAsString)
}

@Test
fun testGetProductById() {
val result =
mockMvc
.perform(get("/api/products/1").accept(MediaType.APPLICATION_JSON))
Expand All @@ -65,31 +95,45 @@ class ProductControllerTest {
}

@Test
fun testPostReview() {
val product =
Product(
"Test Product",
"A test product",
Product.Category.TEE_SHIRT,
13.37,
emptyList(),
mutableListOf<Rating>(),
1L
)
fun testPostReviewForProduct() {
`when`(sentimentService.getSentiment("Super awesome product"))
.thenReturn(SentimentService.SentimentResponse("positive", 4.0f, 0.8f))

`when`(productRepo.findById(anyLong())).thenReturn(Optional.ofNullable(product))
`when`(sentimentService.getSentiment("Good product"))
.thenReturn(SentimentService.SentimentResponse("neutral", 0.0f, 0.0f))

val rating = Rating(4, "Good product", null, product)
val rating = Rating(5, "Super awesome product", null, product)
val json = ObjectMapper().writeValueAsString(rating)

mockMvc
.perform(
post("/api/products/1/ratings").contentType(MediaType.APPLICATION_JSON).content(json)
)
.andExpect(status().isOk)
val result =
mockMvc
.perform(
post("/api/products/1/ratings")
.contentType(MediaType.APPLICATION_JSON)
.content(json)
)
.andExpect(status().isOk)
.andReturn()

verify(ratingRepo, times(1)).save(Rating(4, "Good product", "neutral", product))
// inspect what ratingRepository.save was called with
// Capture the argument passed to ratingRepository.save
val captor = ArgumentCaptor.forClass(Rating::class.java)
verify(ratingRepository).save(captor.capture())

// Assert that the captured argument has the expected values
val savedRating = captor.value
assertEquals(5, savedRating.stars)
assertEquals("Super awesome product", savedRating.comment)
assertEquals("positive", savedRating.sentiment)
}

@Test
fun testGetReviewsForProduct() {
val result =
mockMvc
.perform(get("/api/products/1/ratings").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk)
.andReturn()

val expected =
"""[{"stars":5,"comment":"Great product","sentiment":"positive","id":1},{"stars":1,"comment":"Bad product","sentiment":"negative","id":2},{"stars":3,"comment":"Average product","sentiment":"neutral","id":3},{"stars":4,"comment":"Good product","sentiment":"neutral","id":4}]"""
assertEquals(expected, result.response.contentAsString)
}
}

0 comments on commit 3fc0493

Please sign in to comment.