DEV Community

Jan Cizmar for Tolgee

Posted on

Testing file Download ⬇️ in Spring πŸƒ Boot

It's pretty common case to generate file on the backend and let user to download it. But how to test it? I found testing of this scenario not quite well documented. So here's my bit.

We have to use getAsyncResult method from MvcResult interface, which enables us to retrieve content provided using StreamingResponseBody. So first, let's look how to generate file in the controller.

I am going to use Kotlin btw. 😎

Streaming body with StreamingResponseBody

Let's say we want to enable user to download some fine, which is not very small. For the demo I will just generate a text file containing a lot of a characters.

@Controller
@RequestMapping(value = ["/api/v1/03/get-file"])
class FileProducingController {
    @GetMapping(value = [""], produces = ["application/text"])
    fun export(): ResponseEntity<StreamingResponseBody> {

        return ResponseEntity
                .ok()
                // set a filename
                .header(
                        "Content-Disposition",
                        """attachment; filename="%file.txt""""
                )
                .body(
                        // stream the body
                        StreamingResponseBody { out: OutputStream ->
                            val text = "a".repeat(1000000)
                            out.write(text.toByteArray())
                            out.close()
                        }
                )
    }
}
Enter fullscreen mode Exit fullscreen mode

So what I am doing here is that I am setting the file name, which is the default when user downloads it in a browser. And then I am writing the text to the StreamResponseBody's output.

Finally the testing part

So when I want to test it I have to do this.

@SpringBootTest
@AutoConfigureMockMvc
internal class FileProducingControllerTest {
    @field:Autowired
    lateinit var mvc: MockMvc

    @Test
    fun `produces correct body`() {
        val result = mvc.perform(
                MockMvcRequestBuilders.get("/api/v1/03/get-file")
        )
                .andDo { it.asyncResult }
                .andReturn()
        val content = result.response.contentAsString
        assertEquals("a".repeat(1000000), content)
    }
}
Enter fullscreen mode Exit fullscreen mode

Notice the part with it.asyncResult. Without this part the test would fail. There will be just part of the output in the response's contentAsString result, because it won't wait for the stream to end. You can try this by removing the part and see it fails.


Tolgee is an open-source solution for software localization. It saves developer's time. Go to Tolgee.io and have fun!

Image description

Discussion (2)

Collapse
bemygreenheart profile image
bemygreenheart

Really bad example.

Collapse
jancizmar profile image
Jan Cizmar Author

Why so?