첨부파일 업로드 테스트
먼저 윈도우 서버에 첨부파일을 업로드하는 Service 계층의 테스트를 진행해보았다.
ServerFileService의 upload method
public List<AttachFileDto> uploadFile(List<MultipartFile> uploadFile) {
List<AttachFileDto> fileDtoList = new ArrayList<>();
String uploadFolder = "C:\\upload";
String uploadFolderPath = getFolder();
File uploadPath = new File(uploadFolder, uploadFolderPath);
if (uploadPath.exists() == false) {
uploadPath.mkdirs();
}
for (MultipartFile multipartFile : uploadFile) {
AttachFileDto attachFileDto = new AttachFileDto();
String uploadFileName = multipartFile.getOriginalFilename();
uploadFileName = uploadFileName.substring(uploadFileName.lastIndexOf("\\") + 1);
attachFileDto.setFileName(uploadFileName);
UUID uuid = UUID.randomUUID();
uploadFileName = uuid.toString() + "_" + uploadFileName;
try {
File saveFile = new File(uploadPath, uploadFileName);
multipartFile.transferTo(saveFile);
attachFileDto.setUuid(uuid.toString());
attachFileDto.setUploadPath(uploadFolderPath);
if (checkImageType(saveFile)) {
attachFileDto.setImage(true);
FileOutputStream thumbnail = new FileOutputStream(new File(uploadPath, "s_" + uploadFileName));
Thumbnailator.createThumbnail(multipartFile.getInputStream(), thumbnail, 100, 100);
thumbnail.close();
}
fileDtoList.add(attachFileDto);
} catch (Exception e) {
e.printStackTrace();
}
}
return fileDtoList;
}
위 코드에는 아래의 여러가지 기능들이 한꺼번에 들어가있다.
1) 상위 디렉터리(uploadFolder)와 날짜를 기준으로 구분한 하위 디렉터리(uploadFolderPath)로 업로드할 폴더 매핑
2) 각 MulitpartFile에 해당하는 정보를 추출해, 혹은 random한 UUID를 만들어서 생성한 AttachFileDto 객체에 정보 주입한다.
3) uploadPath, uploadFileName으로 생성한 File 객체가 이미지 타입인 경우, Thumbnailator 라이브러리를 활용해 thumbnail을 생성해준다.
추후에 DB에 저장하도록 Repository를 설계하고, AttachFileDto를 저장하도록 바꾸어야 함.
@Data
public class AttachFileDto {
private String uuid;
private String uploadPath;
private String fileName;
private boolean isImage;
private String contentType;
public String getExtension(){
return StringUtils.getFilenameExtension(fileName);
}
}
Service 테스트
ServerFileServiceTest에서는 getMockMultipartFile 클래스를 사용해 가짜 multipartFile 객체를 만들어주고,
MockMultipartFile file1;
MockMultipartFile file2;
@BeforeEach
void setUp(){
mockMvc = MockMvcBuilders.standaloneSetup(serverFileService).build();
try {
file1 = getMockMultipartFile("2022-1_신입부원_명단", "docx", "C:\\local\\localWordFile.docx");
file2 = getMockMultipartFile("IBAS_로고", "jpg", "C:\\local\\localImageFile.JPG");
} catch (IOException e) {
e.printStackTrace();
}
}
@DisplayName("파일을 업로드하여 서버에 저장한다.")
@Test
public void 파일_업로드() {
// given
List<MultipartFile> multipartFiles = new ArrayList<>();
multipartFiles.add(file1);
multipartFiles.add(file2);
// when
List<AttachFileDto> attachFileDtoList = serverFileService.uploadFile(multipartFiles);
// then
assertThat(attachFileDtoList).extracting("fileName").contains("2022-1_신입부원_명단.docx", "IBAS_로고.jpg");
}
실제로 서버에 있는 파일을 가져와 서비스 단에서 테스트해보았다.
docx 파일과 jpg파일, 그리고 Thumbnail JPG 파일이 생성되는 것을 확인했다.
썸네일 파일은 내가 지정한 크기와 이름으로 생성되었다.
Controller 테스트
Using Spring MVC Test to unit test multipart POST request - Stack Overflow
위 링크를 참고하여 작성하였다.
ServerFileController의 upload method
@PostMapping(
value = "/uploadFile",
consumes = { MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE},
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<AttachFileDto>> uploadFile(@RequestPart List<MultipartFile> uploadFile) {
return new ResponseEntity<>(fileService.uploadFile(uploadFile), HttpStatus.OK);
}
List<AttachFileDto>를 반환하며 Status가 Ok가 되도록 테스트해주면 된다.
@RequestPart를 이용해 multipart/form-data 요청을 받아준다.
consumes 속성을 이용해 RequestBody에 담기는 요청의 타입을 제한할 수 있다.
반대로 produces는 출력하고자 하는 데이터 포맷을 정하는 것이다.
@Test
void 파일_업로드_테스트() {
// given
MockMultipartFile jsonFile = new MockMultipartFile("json", "", "application-json", "{\"json\":\"someValue\"}".getBytes());
// when
try {
mvc.perform(MockMvcRequestBuilders.multipart("/uploadFile")
.file(file1)
.file(file2)
.file(jsonFile)
.andExpect(status().isOk())
.andExpect(content().string("success"));
} catch (Exception e) {
e.printStackTrace();
}
}
이렇게 테스트 코드를 작성하고 1차 실행해보았으나 400 오류코드가 떴다
400에러는 Bad Request 요청이 잘못된 경우이다.
uploadFile이라는 path는 잘못되지 않았고, file을 전달하는 과정에서 오류가 있는 걸로 예상하였다.
에러 로그에서 required request part 'file' is not present 라는 에러 로그를 발견했고,
설마 파일 객체가 안 넘어가나 싶어서 RequestPart에 (required = false) 속성을 넣었더니 테스트가 통과하였다.
(이마 탁)
이 링크의 내용을 정리 하면,
MockMultipartFile file = new MockMultipartFile("path", "url", MediaType.APPLICATION_JSON_VALUE, image);
MockMultipartFile file = new MockMultipartFile("file", "url", MediaType.APPLICATION_JSON_VALUE, image);
위 코드를 아래로 바꿔쓰라는 컨펌이다.
@PostMapping(value = "/upload")
public ResponseEntity<LogoDto> uploadLogo(@RequestParam("file") MultipartFile multipartFile) {
return ResponseEntity.ok(logoService.createLogo(multipartFile));
}
첫번쨰 파라미터는 @RequestParam의 param과 일치해야한다.
getMockMultipartFile에서 아래의 정보를 렌더링 해 돌려보내주는데,
fileName이 uploadFileName이어야 하는건가?
file3 = new MockMultipartFile("uploadFile", "2022-1_신입부원_명단", MediaType.APPLICATION_PDF_VALUE, "<<pdf data>>".getBytes(StandardCharsets.UTF_8));
file4 = new MockMultipartFile("uploadFile", "IBAS_로고.jpg", "image/jpg", new FileInputStream("C:\\local\\localImageFile.JPG"));
file3, file4처럼 첫 파라미터를 내가 Controller에서 지정한 parameter name으로 변경해주었더니 Controller에 잘 꽂혔다.
@DisplayName("파일을 업로드하는 POST 요청을 보낸다.")
@Test
void 파일_업로드_테스트() {
// given
List<AttachFileDto> fileDtoList = new ArrayList<>();
fileDtoList.add(new AttachFileDto(UUID.randomUUID().toString(), "C:\\upload\\" + getFolder(), "사용자가 입력한 파일 이름.docx", false));
fileDtoList.add(new AttachFileDto(UUID.randomUUID().toString(), "C:\\upload\\" + getFolder(), "사용자가 입력한 파일 이름.jpg",true));
given(fileService.uploadFile(anyList())).willReturn(fileDtoList);
// when
String responseBody = null;
try {
responseBody = mvc.perform(multipart("/uploadFile")
.file(file3)
.file(file4))
.andExpect(status().isOk())
.andReturn()
.getResponse().getContentAsString();
} catch (Exception e) {
e.printStackTrace();
}
// then
try {
assertThat(responseBody).isEqualTo(objectMapper.writeValueAsString(fileDtoList));
} catch (Exception e) {
e.printStackTrace();
}
}
여기까지 고쳐 쓴 코드이다.
문제는 responseBody가 null이 뜬다는 것인데 given으로 Mocking하는게 잘 안된 것 같아 다시 검토해보았다.
given의 anyList()를 any()로 고치니까 잘 돌아감!
"[{"uuid":"b35ba9cc-7431-4414-9e14-97f8ee7e9ba5","uploadPath":"C:\\upload\\2022\\03","fileName":"사용자가 입력한 파일 이름.docx","extension":"docx","image":false},{"uuid":"9b5007c5-7a79-460f-b19e-eec1cae1501c","uploadPath":"C:\\upload\\2022\\03","fileName":"사용자가 입력한 파일 이름.jpg","extension":"jpg","image":true}]"
"[{"uuid":"b35ba9cc-7431-4414-9e14-97f8ee7e9ba5","uploadPath":"C:\\upload\\2022\\03","fileName":"ì¬ì©ìê° ì
ë ¥í íì¼ ì´ë¦.docx","extension":"docx","image":false},{"uuid":"9b5007c5-7a79-460f-b19e-eec1cae1501c","uploadPath":"C:\\upload\\2022\\03","fileName":"ì¬ì©ìê° ì
ë ¥í íì¼ ì´ë¦.jpg","extension":"jpg","image":true}]"
이렇게 에러 로그가 뜨면서 UTF8 관련 이슈가 발생했는데
https://goddaehee.tistory.com/248
여기 있는 거 다해봐도 해결이 안되네요
그냥 영어로 적어서 해결 !!
이렇게 첨부파일 업로드 테스트를 완료했다
'Framework > Spring' 카테고리의 다른 글
[1주차] 스프링 부트 테스트 코드 작성, JPA로 데이터베이스 접근하기 (0) | 2022.05.08 |
---|---|
Spring 첨부파일 다운로드 코드 리팩토링 (0) | 2022.03.28 |
Spring File Upload 구현 과정 / Controller에서 파일 데이터 받아오기 (0) | 2022.03.21 |
[IBAS] S3 Upload 코드 리팩토링 (0) | 2022.03.21 |
[IBAS] Spring MVC의 Front Controller Pattern 구현하기 (0) | 2022.03.14 |