해결된 질문
작성
·
310
1
안녕하세요! 강의를 완강 후 혼자 프로젝트를 진행하다 도서 대출 코드를 보고 비슷하게 구현한 사용자가 채용공고를 지원하는 메소드를 호출시 테스트 코드에서 NPE가 발생하는데 혹시 이유를 알 수 있을까요? 여러가지 서칭해봐도 해결이 안되서 질문드립니다... ㅠㅠ
회원entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "users")
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long id;
private String name;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ApplyHistory> applyHistory = new ArrayList<>();
public void applyCompany(JobPosting jobPosting) {
this.applyHistory.add(new ApplyHistory(this, jobPosting));
}
@Builder
private User(Long id, String name, List<ApplyHistory> applyHistory) {
this.id = id;
this.name = name;
this.applyHistory = applyHistory;
}
}
ApplyHistory entity(JobPosting과 user객체가 N:M 매핑해주는 entity)
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "apply_history")
@Entity
public class ApplyHistory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "apply_history_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "job_posting_id")
private JobPosting jobPosting;
public ApplyHistory(User user, JobPosting jobPosting) {
this.user = user;
this.jobPosting = jobPosting;
}
}
JobPosting Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "job_posting")
@Entity
public class JobPosting {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "job_posting_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "company_id")
private Company company;
@Column(name = "posting_position")
private String position;
private int compensation; //채용보상금
@Column(name = "posting_details")
private String postingDetails;
@Column(name = "technology_used")
private String technologyUsed;
@Builder
private JobPosting(Company company, String position, int compensation, String postingDetails,
String technologyUsed) {
this.company = company;
this.position = position;
this.compensation = compensation;
this.postingDetails = postingDetails;
this.technologyUsed = technologyUsed;
}
public void updateJobPosting(String position, int compensation, String postingDetails,
String technologyUsed) {
this.position = position;
this.compensation = compensation;
this.postingDetails = postingDetails;
this.technologyUsed = technologyUsed;
}
}
applyService
@RequiredArgsConstructor
@Service
public class ApplyService {
private final JobPostingRepository jobPostingRepository;
private final UserRepository userRepository;
private final ApplyHistoryRepository userJobPostingRepository;
@Transactional
public void applyCompany(ApplyCompanyRequest request) {
// 1. 채용공고 정보 찾기
JobPosting jobPosting = jobPostingRepository.findById(request.getJobPostingId())
.orElseThrow(() -> new ResourceNotFoundException("jobPosting", request.getJobPostingId()));
// 2. 유저 정보 가져오기
User user = userRepository.findById(request.getUserId())
.orElseThrow(() -> new ResourceNotFoundException("user", request.getUserId()));
// 3. 지원 유무 확인
// 3-1. 지원 중이면 예외 발생
if (userJobPostingRepository.existsByJobPostingAndUser(jobPosting, user)) {
throw new IllegalArgumentException("이미 지원하신 회사입니다.");
}
user.applyCompany(jobPosting);
}
}
리퀘스트
@Getter
@Setter
public class ApplyCompanyRequest {
private Long jobPostingId;
private Long userId;
}
서비스 테스트 코드
@SpringBootTest
class ApplyServiceTest {
@Autowired
JobPostingService jobPostingService;
@Autowired
ApplyService applyService;
@Autowired
JobPostingRepository jobPostingRepository;
@Autowired
UserRepository userRepository;
@Autowired
ApplyHistoryRepository applyHistoryRepository;
@Autowired
CompanyRepository companyRepository;
@AfterEach
void tearDown() {
applyHistoryRepository.deleteAllInBatch();
jobPostingRepository.deleteAllInBatch();
userRepository.deleteAllInBatch();
companyRepository.deleteAllInBatch();
}
@DisplayName("사용자는 채용 공고를 지원 할 수 있다.")
@Test
@Transactional
void applyCompany() {
//given
User user = User.builder()
.id(1L)
.name("jw")
.build();
User savedUser = userRepository.save(user);
Company company = Company.builder()
.name("company1")
.country(Country.KOREA)
.city(City.SEOUL)
.build();
Company savedCompany = companyRepository.save(company);
JobPosting jobPosting = JobPosting.builder()
.company(savedCompany)
.position("백엔드")
.postingDetails("백엔드 개발자 채용합니다.")
.compensation(500000)
.technologyUsed("Java")
.build();
JobPosting savedJobPosting = jobPostingRepository.save(jobPosting);
ApplyCompanyRequest request = new ApplyCompanyRequest();
request.setUserId(savedUser.getId());
request.setJobPostingId(savedJobPosting.getId());
//when
applyService.applyCompany(request);
//then
}
}
-> 이부분에서 applyCompany(request) 호출 시 NPE가 발생합니다.
java.lang.NullPointerException
at com.wanted.findjob.domain.user.User.applyCompany(User.java:36)
at com.wanted.findjob.api.service.ApplyService.applyCompany(ApplyService.java:39)
at com.wanted.findjob.api.service.ApplyService$$FastClassBySpringCGLIB$$2f4064b0.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:792)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:707)
at com.wanted.findjob.api.service.ApplyService$$EnhancerBySpringCGLIB$$81701d47.applyCompany(<generated>)
at com.wanted.findjob.api.service.ApplyServiceTest.applyCompany(ApplyServiceTest.java:83)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
테스트코드가 아닌 직접 서버를 작동해서 api를 호출 시 정상적으로 db에 들어가는 걸 볼 수 있는데 어디가 문제 인지를 모르겠습니다.. ㅠ
답변 2
1
User Entity에서 Bulider패턴 사용시 List<ApplyHistory> applyHistory 초기화 부분문제 때문에 일어난 거였습니다.. 혹시라도 비슷한 경험을 하시는 분들을 위해 질문은 남겨 놓을게요 감사합니다!
0
안녕하세요 정우님!! 와우~ 해결하셨군요!! 다행입니다~ 😊
추가적으로 테스트를 하다가 뭔가 동작이 이상하면
중간에 System.out.println()을 사용하시거나 (약간 비추천)
IntelliJ 디버깅 기능을 활용하셔서 (매우 추천)
DB의 데이터를 확인해보실 수 있습니다.
예를 들어, 테스트 중간에 ApplyHistoryRepository에 접근해보는 것이죠!
혹시나 또 어려운 점 있으시면 편하게 질문 남겨주세요.
감사합니다! 🙇
친절한 답변에 링크 영상까지 정말 감사합니다.!!