오랜만에 MockMvc + RestDocs로 API 문서를 만들려고 하다보니 다 까먹어서 에러를 만나게되었다.
에러에 대한 해결 방법까지 ㄱㄱ
package com.kaii.dth.presentation.student;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kaii.dth.domaincore.domain.shared.Expression;
import com.kaii.dth.domaincore.domain.shared.RegistrationStatus;
import com.kaii.dth.domaincore.domain.shared.UserRole;
import com.kaii.dth.domaincore.domain.student.Student;
import com.kaii.dth.domaincore.domain.student.StudentResult;
import com.kaii.dth.domaincore.infrastructure.exception.message.ResponseMessage;
import com.kaii.dth.port.user.UserReaderPort;
import com.kaii.dth.presentation.shared.ControllerTest;
import com.kaii.dth.presentation.student.dto.StudentDTO;
import com.kaii.dth.usecase.student.StudentUseCase;
import com.kaii.dth.util.TokenUtil;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.Test;
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.http.MediaType;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import static com.kaii.dth.common.ApiDocumentUtils.getDocumentRequest;
import static com.kaii.dth.common.ApiDocumentUtils.getDocumentResponse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.put;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(StudentController.class)
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
public class StudentControllerTest extends ControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@MockBean
private StudentUseCase studentUseCase;
@MockBean
private UserReaderPort userReaderPort;
@MockBean
private TokenUtil tokenUtil;
@Test
@WithMockUser
void 학생_마이페이지_수정_테스트() throws Exception {
// given
String accessToken = "ThisIsAccessToken";
Student student = Student.builder()
.userId(1L)
.role(UserRole.ROLE_STUDENT)
.loginId("S000001")
.password("2023")
.verificationNum(null)
.verifyNumSendCnt(Expression.VERIFICATION_NUM_SEND_CNT_DEFAULT)
.verifyNumIssuanceTime(null)
.name("임학생")
.email("email@email.com")
.phoneNum("01000000000")
.isFirstSetPw(Expression.IS_FIRST_SET_PW_DEFAULT)
.isAgreeTos(Expression.IS_AGREE_TOS_DEFAULT)
.agreeTosDate(null)
.loginTryCnt(Expression.LOGIN_TRY_COUNT_DEFAULT)
.pwChangeDate(null)
.refreshToken(null)
.deactivatedAt(null)
.registrationStatus(RegistrationStatus.ATTENDING)
.year(2023)
.semester(1)
.schoolYear(1)
.patientList(null)
.build();
StudentDTO.EditReuest editReuest = new StudentDTO.EditReuest("studentEditTest@email.com", "01020240104");
given(여기!!!).willReturn(여기);
// when
mockMvc.perform(
put("/student-mypage")
.header("Authorization", accessToken)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(editReuest))
)
.andExpect(status().isOk())
.andExpect(jsonPath("code").value(200))
.andExpect(jsonPath("message").value(ResponseMessage.SUCCESS_MSG))
.andExpect(jsonPath("data").value("임학생 - 수정 완료"))
.andDo(document(
"student-mypage/modify",
getDocumentRequest(),
getDocumentResponse(),
requestHeaders(
headerWithName("Authorization").description("액세스 토큰")
),
requestFields(
fieldWithPath("email").type(JsonFieldType.STRING).description("이메일"),
fieldWithPath("phoneNum").type(JsonFieldType.STRING).description("이메일").optional()
),
responseFields(
fieldWithPath("code").type(JsonFieldType.NUMBER).description("결과 코드"),
fieldWithPath("message").type(JsonFieldType.STRING).description("결과 메세지"),
fieldWithPath("data").type(JsonFieldType.STRING).description("학생(본인) 이름 - 수정 완료")
)
));
}
}
given(studentUseCase.updateMyPage(accessToken, editReuest.toCommand())).willReturn(student);
Request processing failed: java.lang.NullPointerException: Cannot invoke "com.kaii.dth.domaincore.domain.student.Student.getName()" because the return value of "com.kaii.dth.usecase.student.StudentUseCase.updateMyPage(String, com.kaii.dth.domaincore.domain.student.StudentCommand$Edit)" is null
given(studentUseCase.updateMyPage(anyString(), any())).willReturn(any()); //or given(studentUseCase.updateMyPage(any(), any())).willReturn(any());
jakarta.servlet.ServletException: Request processing failed: org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
2 matchers expected, 1 recorded:
-> at com.~~~
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(any(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
//correct:
someMethod(any(), eq("String by matcher"));
에러를 잘 읽어보면 "2개의 Matchers를 예상, 1개만 기록" 이라고 되어있다.
또한 "Matchers가 raw한 값과 결합된 경우 이 예외가 발생할 수 있음" 인데,
Usecase 메서드에 파라미터를 넘겨줄 때 argumentmatcher를 한 인자에 사용하면 다른 모든 인자도 argumentmatcher로 넘겨주어야 한다는 것이다. <라는 글을 보았다.
그러나, 위에서 다 any()로 넘겨주었는데도 에러가 발생한거니..뭐 어떻게 해야하나?
given(studentUseCase.updateMyPage(any(), any())).willReturn(student);
드디어 성공!