오늘.. 해서는 안 되는 "그" 실수를 하고 말았다.
꽤 공들여 작성한 코드인데..
좀 괜찮은 코드 같은데..
어쩌면.. 이번엔 디버깅 버튼은 생략하고..
대신 실행버튼을 눌러봐도 될 지도..? ㅎ
로그를 읽어보고,
코드를 검토해보고, 구글링을 아무리 하더라도
에러를 해결하고나서 가장 중요하다고 느낀 것은
"객관적으로 나의 코드가 정말 올바른지 검토하는 것"
이 부족했구나.. 역시 나의 논리에 대해 더 신중함을 가져야 하는 구나..
라고 나의 한계와 내 코드가 작동하지 않는 이유의 실체를 마주했다.
정말 겸손해야겠다.
같은 실수를 더 이상 반복하지 않기 위해
MockMvc 사용 방법과
가장 기초적인 NullPointException의 발생과정에 대해 취득한 정보를 기록하려고 한다.
public class NullPointerException extends RuntimeException
객체가 필요한 경우에 어플리케이션이 null을 사용하려고 하면 발생 됩니다.
Thrown when an application attempts to use null in a case where an object is required.
These include:
- Calling the instance method of a null object.
- Accessing or modifying the field of a null object.
- Taking the length of null as if it were an array.
- Accessing or modifying the slots of null as if it were an array.
- Throwing null as if it were a Throwable value.
- Applications should throw instances of this class to indicate other illegal uses of the null object.
- NullPointerException objects may be constructed by the virtual machine as if suppression were disabled and/or the stack trace was not writable.
무슨 소린지 이해해보자
int x;
x = 10;
하.지.만
레퍼런스 타입 변수 선언 시 자바의 처리방법은 다르다!
Integer num; // 1
num = new Integer(10); // 2
num이라는 이름으로 선언된 변수는 원시(primitive) 값을 저장하지 않는다.
대신, Integer라는 이름의 타입은 래퍼클래스로서, 레퍼런스타입이므로 해당 변수는 주소 (참조값)을 저장하게 된다.
-> 아직 어떤 것을 참조하라고 정의하지 않았기 때문에 자바는 그 변수를 null로 초기화한다.
-> "나는 아무것도 참조하지 않아!"라는 의미
new라는 키워드를 사용해서 Integer 클래스 객체를 생성하고 해당 객체의 주소 (참조값을) num이라는 변수에 저장하면,
객체를 생성하고 객체의 참조값을 저장한 변수를 사용하여 해당 객체에 접근할 수 있게 된다. (그래서 이 때, . 연산자를 사용)
만약 레퍼런스 타입 변수를 선언하고 객체를 생성하지 않으면 (즉, 객체의 참조값을 해당 변수에 저장하지 않으면) Exception이 발생
-> 객체가 생성되기 전 num 변수를 사용해서 해당 클래스의 객체를 접근하고자 하면 NullPointerException이 발생!
이러한 경우 대부분 컴파일러가 해당 문제를 인식해서 경로 메시지로 알려주게 된다. "num 변수가 아직 초기화되지 않았어"라고 ..
만약 이런 메서드를 다음과 같이 호출시
public void doSomething(Integer num) { // do something to num }
public static void main(String[] args){
doSomething(null);
}
null 값을 가지는 num 변수를 사용해서 객체의 필드 혹은 메서드에 접근하고자 한다면 NullPointerException이 발생하게 되는 원리였다..
이와 같은 exception이 발생되지 않게 하는 최선의 방법은
레퍼런스 변수를 사용하기 전에 null 값을 저장하고 있는지를 체크하는 것이라고 한다.
public void doSomething(Integer num) {
if(num != null) {
// do something to num
}
}
내가 사용하는 객체가 초기화 되지 않는 경우를 검증한 뒤에도
MockMVC 사용에 대한 지식이 없어서 삽질을 2시간 정도 했다..
공식 주석은 다음과 같다.
"Main entry point for server-side Spring MVC test support."
//Example
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
// ...
WebApplicationContext wac = ...;
MockMvc mockMvc = webAppContextSetup(wac).build();
mockMvc.perform(get("/form"))
.andExpect(status().isOk())
.andExpect(content().mimeType("text/html"))
.andExpect(forwardedUrl("/WEB-INF/layouts/main.jsp"));
아주 간결하고 군더더기 없는 설명하지만 좀 더 음미하기 위해 배경을 살펴보자면,
컨트롤러를 테스트하는데 사용되는
어노테이션을 통한 2가지 세팅 방법을 알게 되었다.
JUnit 5.x 부터는 @RunWith가 아닌 @ExtendWith를 사용한다.
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
class ApplicationTests {
@Autowired
MockMvc mockMvc;
@Autowired
WebApplicationContext webApplicationContext;
@Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.alwaysDo(print())
.build();
}
@Test
public void testCodeSample() throws Exception {
mockMvc.perform(get("/address")
.param("param_1", "0"))
.andExpect(status().isOk())
.andExpect(content().string("Need reponse text"))
.andReturn();
}
. . .
}
테스트 함수 실행전 공통으로 적용할 요소들을 셋업한다. alwayDo(print()) 체이닝 함수는, 모든 테스트 함수마다 결과를 자세하게 출력하게끔 설정해 놓은 것이다.
JUnit 5.x에서는 @BeforeEach를 사용한다. @After, @AfterEach는 실행후 처리에 대해 지정할 수 있다.
get(“/address”).param()
post(“/address”).content()
andExpect()Permalink
perform()으로 발생한 요청에 대한 응답 어떤 기대값을 가져야하는지 지정한다.
var response = mockMvc.perform(get("/address")
.param("parma_1", "0"))
.andExpect(status().isOk())
.andExpect(content().string("Need reponse text"))
.andDo(print())
.andReturn();
위 샘플 코드는 Http Response Code가 200인지,
응답 content에 “Need reponse text” 문자열이 있는지 검증한다.
또한 andExpect(jsonPath(“$.field”).value(“success”))처럼 응답 JSON의 특정 경로에 원하는 값이 있는지 확인 가능하다.
MockMVC.perform()으로 리턴되는 ResultAction 인터페이스에 대한 처리를 지정한다.
보통 andDo(print())를 많이 쓰는데,
많이 쓰는 만큼 @Before와 setUp() 함수에서 미리 지정해놓으면 편하다고 한다.
andReturn()은 응답 객체를 그대로 재사용할 수 있도록 해준다.
다음을 참조하여 작성하였습니다. 정말 감사합니다. 🙇🏻♂️