spring-projects / spring-projects/spring-data-commons

MockMVC with PagingAndSortingRepository - put with Content-Type set to text/uri-list does not save relation

Open
#2,350 2 comments 0 reactions 1 assignee View on GitHub

@odrotbohm is already working on this.

Since Apr 12, 2021.

status: feedback-provided
Dominant language
Java
Stars
838
Forks
730
PR merge metrics
No merged PRs in 30d

Description

I have some repositories and relations setup and it does not seem like MockMVC works with the text/uri-list PUT correctly.

I have it working with TestRestTemplates in a test where the server starts, but for some reason the MockMVC does not setup the relation properly. Is there additional test setup to use MockMVC with PagingAndSortingRepository?

Failing test (I get 404 for the last line):

package com.saisols.maint.integration

import com.fasterxml.jackson.databind.ObjectMapper
import com.saisols.maint.domain.Equipment
import com.saisols.maint.domain.Manufacturer
import com.saisols.maint.domain.Tag
import com.saisols.maint.domain.Vendor
import com.saisols.maint.getEquipment
import com.saisols.maint.getManufacturer
import com.saisols.maint.getTag
import com.saisols.maint.getVendor
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print

@SpringBootTest
@AutoConfigureMockMvc
class MockMVCRESTTest {
    @Autowired
    private lateinit var mockMvc: MockMvc

    @Autowired private lateinit var objectMapper: ObjectMapper

    @Test
    fun testGetRoot(){
        this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
    }

    fun test_whenCreateManufacturer_thenSuccess_andReturnLocation(): String {
        val manufacturer = getManufacturer()
        val response = this.mockMvc.perform(post("/manufacturer").content(objectMapper.writeValueAsString(manufacturer)))
            .andDo(print()).andExpect(status().`is`(201)).andReturn()
        val location = response.response.getHeader("Location")
        assertNotNull(location)
        return location!!
    }

    fun test_whenCreateVendor_thenSuccess_andReturnLocation(): String {
        val vendor = getVendor()
        val response = this.mockMvc.perform(post("/vendor").content(objectMapper.writeValueAsString(vendor)))
            .andDo(print()).andExpect(status().`is`(201)).andReturn()
        val location = response.response.getHeader("Location")
        assertNotNull(location)
        return location!!
    }

    fun test_whenCreateTag_thenSuccess_andReturnLocation(): String {
        val response = this.mockMvc.perform(post("/tag").content(objectMapper.writeValueAsString(getTag())))
            .andDo(print()).andExpect(status().`is`(201)).andReturn()
        val location = response.response.getHeader("Location")
        assertNotNull(location)
        return location!!
    }

    fun test_whenCreateEquipment_thenSuccess_andReturnLocation(): String {
        val response = this.mockMvc.perform(post("/equipment").content(objectMapper.writeValueAsString(getEquipment())))
            .andDo(print()).andExpect(status().`is`(201)).andReturn()
        val location = response.response.getHeader("Location")
        assertNotNull(location)
        return location!!
    }
    
    @Test
    fun test_whenCreateVendorTag_thenCanFetchEach(){
        val vendorLocation = test_whenCreateVendor_thenSuccess_andReturnLocation()
        val vendor = objectMapper.readValue(this.mockMvc.perform(get(vendorLocation)).andExpect(status().
        `is`(200)).andReturn().response.contentAsString, Vendor::class.java)

        val tagLocation = test_whenCreateTag_thenSuccess_andReturnLocation()
        val tag = objectMapper.readValue(this.mockMvc.perform(get(tagLocation)).andExpect(status().
        `is`(200)).andReturn().response.contentAsString, Tag::class.java)

        val manufacturerLocation = test_whenCreateManufacturer_thenSuccess_andReturnLocation()
        val manufacturer = objectMapper.readValue(this.mockMvc.perform(get(manufacturerLocation)).andExpect(status().
        `is`(200)).andReturn().response.contentAsString, Manufacturer::class.java)

        val equipmentLocation = test_whenCreateEquipment_thenSuccess_andReturnLocation()
        val equipment = objectMapper.readValue(this.mockMvc.perform(get(equipmentLocation)).andExpect(status().
        `is`(200)).andReturn().response.contentAsString, Equipment::class.java)

        val equipmentManufacturerEndpoint = "${equipmentLocation}/manufacturer"
        var putResult = this.mockMvc.perform(
            put (equipmentManufacturerEndpoint).
        contentType("Content-Type:text/uri-list").content(manufacturerLocation))

       putResult.andExpect(status().`is`(200)) // Fails with 404
    }
}

Here is a passing test using TestRestTemplate:

package com.saisols.maint.integration

import com.saisols.maint.domain.Manufacturer
import com.saisols.maint.domain.Vendor
import com.saisols.maint.getEquipment
import com.saisols.maint.getManufacturer
import com.saisols.maint.getVendor
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment
import org.springframework.boot.test.web.client.TestRestTemplate
import org.springframework.boot.test.web.client.getForEntity
import org.springframework.boot.web.server.LocalServerPort
import org.springframework.http.HttpEntity
import org.springframework.http.HttpHeaders
import java.net.URI

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class HttpRequestTest {
    @LocalServerPort
    private var port: Int = 0

    @Autowired
    private lateinit var restTemplate: TestRestTemplate

    fun getUrlWithEndpoint(endpoint: String = ""): String {
        var slash = "/"
        if (endpoint.endsWith("/")) {
            slash = ""
        }
        return "http://localhost:${port}${slash}${endpoint}"
    }

    @Test
    fun rootShouldContainLinks() {
        val rootResponse = restTemplate.getForObject(
            getUrlWithEndpoint(),
            String::class.java
        )
        arrayOf("_links", "vendor", "manufacturer").forEach {
            assertThat(
                rootResponse
            ).contains(it)
        }
    }

    @Test
    fun testCrateEquipment() {
        createEquipment()
    }

    @Test
    fun testCrateManufacturer() {
        createEquipment()
    }

    @Test
    fun testAssociateEquipmentAndRelatedItems() {
        val equipmentLocation = createEquipment()
        val manufacturerLocation = createManufacturer()
        val vendorLocation = createVendor()

        val headers = HttpHeaders()
        headers.set("Content-Type", "text/uri-list")

        val entity: HttpEntity<String> = HttpEntity<String>(manufacturerLocation.toString(), headers)
        val equipmentManufacturer = "${equipmentLocation}/manufacturer"
        val associateResponse = restTemplate.put(
            equipmentManufacturer,
            entity
        )
        assertNotNull(associateResponse)

        val entityVendor: HttpEntity<String> = HttpEntity<String>(vendorLocation.toString(), headers)
        val equipmentVendor = "${equipmentLocation}/vendor"
        val associateResponse2 = restTemplate.put(
            equipmentVendor,
            entityVendor
        )
        assertNotNull(associateResponse2)

        val getResponseVendor = restTemplate.getForEntity(
            URI(equipmentVendor),
            Vendor::class.java)
        assertNotNull(getResponseVendor)
        assertEquals(getResponseVendor.statusCodeValue, 200)

        val getResponseManufacturer = restTemplate.getForEntity(
            URI(equipmentManufacturer),
            Manufacturer::class.java)
        assertNotNull(getResponseManufacturer)
        assertEquals(getResponseManufacturer.statusCodeValue, 200)
    }

    fun createVendor(): URI {
        val createResponse = restTemplate.postForLocation(
            getUrlWithEndpoint("/vendor"),
            getVendor()
        )
        assertNotNull(createResponse)
        return createResponse;
    }

    fun createManufacturer(): URI {
        val createResponse = restTemplate.postForLocation(
            getUrlWithEndpoint("/manufacturer"),
            getManufacturer()
        )
        assertNotNull(createResponse)
        return createResponse;
    }

    fun createEquipment(): URI {
        val createResponse = restTemplate.postForLocation(
            getUrlWithEndpoint("/equipment"),
            getEquipment()
        )
        assertNotNull(createResponse)
        return createResponse;
    }
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.