Spring Boot Web Slice测试–示例

春天开机推出 测试切片而回,它已经采取了一些时间来解决它我的头,并探讨一些细微的差别。

背景

使用此功能的主要原因是减少样板。 考虑一个看起来像这样的控制器,仅适用于使用Kotlin编写的各种控制器。

@RestController
@RequestMapping("/users")
class UserController(
        private val userRepository: UserRepository,
        private val userResourceAssembler: UserResourceAssembler) {

    @GetMapping
    fun getUsers(pageable: Pageable, 
                 pagedResourcesAssembler: PagedResourcesAssembler<User>): PagedResources<Resource<User>> {
        val users = userRepository.findAll(pageable)
        return pagedResourcesAssembler.toResource(users, this.userResourceAssembler)
    }

    @GetMapping("/{id}")
    fun getUser(id: Long): Resource<User> {
        return Resource(userRepository.findOne(id))
    }
}

用于测试此控制器的传统Spring Mock MVC测试将遵循以下原则:

@RunWith(SpringRunner::class)
@WebAppConfiguration
@ContextConfiguration
class UserControllerTests {

    lateinit var mockMvc: MockMvc

    @Autowired
    private val wac: WebApplicationContext? = null

    @Before
    fun setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build()
    }

    @Test
    fun testGetUsers() {
        this.mockMvc.perform(get("/users")
                .accept(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isOk)
    }

    @EnableSpringDataWebSupport
    @EnableWebMvc
    @Configuration
    class SpringConfig {

        @Bean
        fun userController(): UserController {
            return UserController(userRepository(), UserResourceAssembler())
        }

        @Bean
        fun userRepository(): UserRepository {
            val userRepository = Mockito.mock(UserRepository::class.java)
            given(userRepository.findAll(Matchers.any(Pageable::class.java)))
                    .willAnswer({ invocation ->
                        val pageable = invocation.arguments[0] as Pageable
                        PageImpl(
                                listOf(
                                        User(id = 1, fullName = "one", password = "one", email = "one@one.com"),
                                        User(id = 2, fullName = "two", password = "two", email = "two@two.com"))
                                , pageable, 10)
                    })
            return userRepository
        }
    }
}

设置这样的测试涉及很多仪式-理解Web环境的Web应用程序上下文被引入,需要创建设置Spring MVC环境的配置,以及满足测试框架需求的MockMvc在每次测试之前进行设置。

网页切片测试

与以前的测试相比,Web Slice测试要简单得多,它专注于测试控制器并隐藏了许多样板代码:

@RunWith(SpringRunner::class)
@WebMvcTest(UserController::class)
class UserControllerSliceTests {

    @Autowired
    lateinit var mockMvc: MockMvc

    @MockBean
    lateinit var userRepository: UserRepository

    @SpyBean
    lateinit var userResourceAssembler: UserResourceAssembler

    @Test
    fun testGetUsers() {

        this.mockMvc.perform(get("/users").param("page", "0").param("size", "1")
                .accept(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isOk)
    }

    @Before
    fun setUp(): Unit {
        given(userRepository.findAll(Matchers.any(Pageable::class.java)))
                .willAnswer({ invocation ->
                    val pageable = invocation.arguments[0] as Pageable
                    PageImpl(
                            listOf(
                                    User(id = 1, fullName = "one", password = "one", email = "one@one.com"),
                                    User(id = 2, fullName = "two", password = "two", email = "two@two.com"))
                            , pageable, 10)
                })
    }
}

它的工作方式是创建一个Spring Application上下文,但过滤掉与Web层无关的所有内容,并仅加载已传递到@WebTest批注中的控制器。 控制器需要的任何依赖关系都可以作为模拟注入。

涉及到一些细微差别,例如,如果我想自己注入某个字段,则可以使用自定义的Spring Configuration进行测试,对于测试,可以使用内部带有@TestConfiguration注释的静态类来完成。以下方式:

@RunWith(SpringRunner::class)
@WebMvcTest(UserController::class)
class UserControllerSliceTests {

    @Autowired
    lateinit var mockMvc: MockMvc

    @Autowired
    lateinit var userRepository: UserRepository

    @Autowired
    lateinit var userResourceAssembler: UserResourceAssembler

    @Test
    fun testGetUsers() {

        this.mockMvc.perform(get("/users").param("page", "0").param("size", "1")
                .accept(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isOk)
    }

    @Before
    fun setUp(): Unit {
        given(userRepository.findAll(Matchers.any(Pageable::class.java)))
                .willAnswer({ invocation ->
                    val pageable = invocation.arguments[0] as Pageable
                    PageImpl(
                            listOf(
                                    User(id = 1, fullName = "one", password = "one", email = "one@one.com"),
                                    User(id = 2, fullName = "two", password = "two", email = "two@two.com"))
                            , pageable, 10)
                })
    }

    @TestConfiguration
    class SpringConfig {

        @Bean
        fun userResourceAssembler(): UserResourceAssembler {
            return UserResourceAssembler()
        }

        @Bean
        fun userRepository(): UserRepository {
            return mock(UserRepository::class.java)
        }
    }

}

来自“ TestConfiguration”的Bean将添加到Slice测试所依赖的配置中,并且不会完全替换它。

另一方面,如果我想重写带注释的主“ @SpringBootApplication”主类的加载,则可以显式传递一个Spring Configuration类,但要注意的是,我现在必须负责所有相关的加载工作。 Spring Boot具有我自己的功能(启用自动配置,适当的扫描等),因此可以通过以下方式围绕它来显式批注Spring Boot应用程序的配置:

@RunWith(SpringRunner::class)
@WebMvcTest(UserController::class)
class UserControllerExplicitConfigTests {

    @Autowired
    lateinit var mockMvc: MockMvc

    @Autowired
    lateinit var userRepository: UserRepository

    @Test
    fun testGetUsers() {

        this.mockMvc.perform(get("/users").param("page", "0").param("size", "1")
                .accept(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(status().isOk)
    }

    @Before
    fun setUp(): Unit {
        given(userRepository.findAll(Matchers.any(Pageable::class.java)))
                .willAnswer({ invocation ->
                    val pageable = invocation.arguments[0] as Pageable
                    PageImpl(
                            listOf(
                                    User(id = 1, fullName = "one", password = "one", email = "one@one.com"),
                                    User(id = 2, fullName = "two", password = "two", email = "two@two.com"))
                            , pageable, 10)
                })
    }

    @SpringBootApplication(scanBasePackageClasses = arrayOf(UserController::class))
    @EnableSpringDataWebSupport
    class SpringConfig {

        @Bean
        fun userResourceAssembler(): UserResourceAssembler {
            return UserResourceAssembler()
        }

        @Bean
        fun userRepository(): UserRepository {
            return mock(UserRepository::class.java)
        }
    }

}

但是要注意的是,现在其他测试可能最终会找到这种不理想的内部配置!因此,我的学习一直依赖于最低限度的最小切片测试,如果需要,可以使用@TestConfiguration对其进行扩展。

我在github仓库中有一些更详细的代码示例,其中包含一些可用的示例。

翻译自: https://www.javacodegeeks.com/2017/06/spring-boot-web-slice-test-sample.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot 中实现切片上传视频,需要进行以下步骤: 1. 前端切片上传:前端将视频文件进行切片,使用 Form Data 将切片文件上传到后端。可以使用 axios 或者 jQuery 的 ajax 进行文件上传。 2. 后端接收切片文件:后端接收前端上传的切片文件,可以使用 Spring Boot 的 MultipartFile 类型接收文件流。 3. 后端合并切片文件:将接收到的切片文件进行合并,生成完整的视频文件。可以使用 RandomAccessFile 类型进行文件合并。 4. 前端进度条展示:前端可以通过监听上传事件,实时展示上传进度条。 5. 后端断点续传:可以为切片上传添加断点续传功能,保证上传的可靠性和完整性。 示例代码如下: 前端代码: ```javascript // 切片上传 function uploadFile() { var file = document.getElementById("file").files[0]; var chunkSize = 1024 * 1024; // 切片大小,这里设置为 1MB var chunks = Math.ceil(file.size / chunkSize); // 切片总数 var currentChunk = 0; // 当前切片编号 var fileReader = new FileReader(); var formData = new FormData(); var xhr = new XMLHttpRequest(); xhr.open("POST", "/video/upload", true); fileReader.onload = function (e) { formData.append("file", new Blob([e.target.result])); formData.append("name", file.name); formData.append("chunks", chunks); formData.append("currentChunk", currentChunk); formData.append("chunkSize", chunkSize); xhr.upload.onprogress = function (event) { if (event.lengthComputable) { var complete = (event.loaded / event.total * 100 | 0); console.log("complete:" + complete + "%"); } }; xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { console.log(xhr.responseText); currentChunk++; if (currentChunk < chunks) { fileReader.readAsArrayBuffer(file.slice(currentChunk * chunkSize, (currentChunk + 1) * chunkSize)); } else { console.log("上传完成"); } } }; xhr.send(formData); }; fileReader.readAsArrayBuffer(file.slice(0, chunkSize)); } ``` 后端代码: ```java @RestController @RequestMapping("/video") public class VideoController { private static final String UPLOAD_DIR = "upload/"; @PostMapping("/upload") public String upload(@RequestParam("file") MultipartFile file, @RequestParam("name") String name, @RequestParam("chunks") int chunks, @RequestParam("currentChunk") int currentChunk, @RequestParam("chunkSize") int chunkSize) { try { File dir = new File(UPLOAD_DIR); if (!dir.exists()) { dir.mkdirs(); } File chunkFile = new File(UPLOAD_DIR + name + "_" + currentChunk); IOUtils.copy(file.getInputStream(), new FileOutputStream(chunkFile)); if (currentChunk == chunks - 1) { File targetFile = new File(UPLOAD_DIR + name); FileOutputStream outputStream = new FileOutputStream(targetFile, true); for (int i = 0; i < chunks; i++) { File tempFile = new File(UPLOAD_DIR + name + "_" + i); FileInputStream inputStream = new FileInputStream(tempFile); byte[] buffer = new byte[(int) tempFile.length()]; inputStream.read(buffer); outputStream.write(buffer); inputStream.close(); tempFile.delete(); } outputStream.close(); } return "success"; } catch (IOException e) { e.printStackTrace(); return "error"; } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值