인프런 커뮤니티 질문&답변

터치벨님의 프로필 이미지

작성한 질문수

스프링 기반 REST API 개발

이벤트 Repository

NullPointException 부근 질문있습니다.

작성

·

823

3

 test의 경우 EventRepository에 save를 하고 난 값이 리턴되지 않기 때문에 Mockito를 이용해서 객체를 그대로 리턴해주는 식으로 이해했습니다. 그런데 이 부분에서 똑같이 NPE가 발생하길래 왜 그런가 봤더니 Event 엔티티의 @EqualsAndHashCode 사용 유무 문제였습니다. 

 여기서 의문이 드는게 @EqualsAndHashCode가 Mockito의 리턴이랑 무슨 관계인지 이해가 안갑니다. 단순히 Test에서 만든 Event 객체를 다시 리턴시키는데 왜 @EqualsAndHashCode가 영향을 미치는 건가요??

답변 14

4

백기선님의 프로필 이미지
백기선
지식공유자

오 생각보다 복잡한 이슈였네요. 

Mockito.when(eventRepository.save(event)).thenReturn(event);

여기 보시면 save에 넘겨주는 event가 있고 그 객체가 넘어오면 event를 리턴하라고 스터빙을 한건데요.
@EAH가 없으면 여기서 목킹하면서 save에 넘겨주는 event  객체랑 컨트롤러 안에서 save 호출할 때 넘겨주는 event 객체랑..

Event newEvent = this.eventRepository.save(event);

서로 다르다고 생각해서 stubbing을 제대로 적용 안하고 (즉 리턴하라고 했던 event)를 넘겨주지 않고.. 그냥 null이 리턴 됩니다. 그래서 newEvent가 null이 되요.

URI createdURi = linkTo(EventController.class).slash(newEvent.getId()).toUri();

그 상태에서 바로 아래 줄인 저기서 getId를 null에다가 호출하니까 NPE가 발생한겁니다.

@EAH를 사용하시거나, equals랑 hashCode를 직접 구현해서 목킹할 때 넘겨준 객체랑 실제 객체가 같은지 확인 될 수 있게끔 해주시거나..

아니면 테스트 코드를 이렇게 바꿔주시면 아무런 Event 타입의 객체만 받아도 항상 event 객체를 리턴하도록 했기 때문에 @EAH가 없어도 테스트는 잘 동작할 겁니다.

Mockito.when(eventRepository.save(any(Event.class))).thenReturn(event);

0

백기선님의 프로필 이미지
백기선
지식공유자

네 정확하게 이해하셨네요.

0

터치벨님의 프로필 이미지
터치벨
질문자

와 한번에 이해한 것 같습니다.

정리하자면 Mockito에서 when()이 일반적인 이벤트리스너 함수처럼 호출되는 메소드만 같다고 작동하는 것이 아니라 실제로 매개변수에 들어가는 객체까지 같아야 작동하는 것이군요..! 그렇기 때문에 객체를 비교하는 equals()와 hashCode()가 필요한거구요!

바쁘신데 명쾌하고 빠른 답변 정말 감사합니다!

 

 

0

터치벨님의 프로필 이미지
터치벨
질문자

아닙니다..ㅎ

웹 개발자 취준생인데 토이프로젝트도 만들고, 복습도 해보자 해서 재수강하고 있었는데, 도무지 이해가 안가서 잠 못들고 있었습니다..ㅎ

0

백기선님의 프로필 이미지
백기선
지식공유자

(근데 왜 이시간에.. 안주무시고?) 혹시 미국에 사세요?

0

백기선님의 프로필 이미지
백기선
지식공유자

감사합니다. 살펴보고 다시 알려드릴께요.

0

터치벨님의 프로필 이미지
터치벨
질문자

넵. 확인해주시기 쉽게 최대한 같은 코드로 올렸습니다.

github : https://github.com/tj3828/RestAPI-NPE

Error :

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException

	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1013)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
	at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:71)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:166)
	at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
	at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
	at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
	at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
	at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:182)
	at com.example.npe.events.EventControllerTest.createBoard(EventControllerTest.java:58)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
	at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.NullPointerException
	at com.example.npe.events.EventController.createEvent(EventController.java:32)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
	... 48 more

0

백기선님의 프로필 이미지
백기선
지식공유자

그리고 NPE 발생한 에러 로그도 복사해서 붙여 주시고 가능하면 소스 코드도 깃헙에 올려주시면 좀 더 살펴보겠습니다.

0

터치벨님의 프로필 이미지
터치벨
질문자

예 맞습니다. @EAH가 있으면 정상 작동합니다.

0

백기선님의 프로필 이미지
백기선
지식공유자

5분30초까지 똑같이 따라서 stubbing을 했는데도 @EAH가 없으면 NPE가 발생한다는 말씀인가요?

0

터치벨님의 프로필 이미지
터치벨
질문자

실제로 강사님과 같은 코드 조건에서 @EAH가 없으면 동작을 하지 않고, 같은 부분에서 똑같이 NPE가 발생합니다. (test에서만)

0

백기선님의 프로필 이미지
백기선
지식공유자

4분 40초에서 NPE가 발생한 이유에 대해 설명을 드렸네요. @EAH랑 상관이 없어 보이는데요. 왜 그렇게 생각하시는거죠?

0

터치벨님의 프로필 이미지
터치벨
질문자

5분 30초 부근입니다. 

test가 아닌 실제 서버에서는 상관없는데,

test의 경우에만 Event에 @EqualsAndHashCode가 없으면 여전히 NPE가 발생합니다.

이 상황을 보면, Mokito의 thenReturn 메소드가 @EqualsAndHashCode와 연관이 있는 것 같다고 생각하는데 그 이유를 모르겠어서 질문드렸습니다.

0

백기선님의 프로필 이미지
백기선
지식공유자

EventRepository에서 save하면 저장된거 리턴 해주는데요? 수업 영장 몇분쯤을 보고 말씀하시는건지 알려주시겠어요?