기존에 jar파일을 추가해서 Junit4로 테스트하던 프로젝트를 마찬가지로 jar파일을 Junit5로 버전 업해서 사용하고 있었는데 ‘굳이 이럴 필요없이 maven 프로젝트로 변경하면 되지 않나?’라는 생각에 maven 프로젝트로 변경 후 발생했던 에러를 해결한 방법을 작성해둔다…
상황
위에서 적은 것 처럼 기존에 Junit jar를 다운받아서 직접 경로를 지정하는 방식으로 junit4, 5를 둘 다 사용하고 있었는데 프로젝트를 maven으로 변경 후 의존성 충돌이 발생해 아래와 같은 에러가 발생하며 테스트가 진행되지 않았다.
발생한 에러
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Internal Error occurred. org.junit.platform.commons.JUnitException: TestEngine with ID 'junit-vintage' failed to discover tests at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:111) at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:85) at org.junit.platform.launcher.core.DefaultLauncher.discover(DefaultLauncher.java:92) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75) at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53) Caused by: org.junit.platform.commons.JUnitException: Unsupported version of junit:junit: 3.8.1. Please upgrade to version 4.12 or later. at org.junit.vintage.engine.JUnit4VersionCheck.checkSupported(JUnit4VersionCheck.java:49) at org.junit.vintage.engine.JUnit4VersionCheck.checkSupported(JUnit4VersionCheck.java:35) at org.junit.vintage.engine.VintageTestEngine.discover(VintageTestEngine.java:62) at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:103) ... 7 more
Process finished with exit code -2
위 에러 로그처럼 ‘junit-vintage’를 아이디로 하는 테스트 엔진을 찾지 못했다는 에러가 발생해서 junit-vintage 의존성을 추가해주었는데..
라고 떡하니 써 적혀있어서 위에 3 개의 의존성을 모두 추가한 후 리빌드 후 다시 테스트를 실행해봤다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
<!-- Only needed to run tests in a version of IntelliJ IDEA that bundles older versions --> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-launcher</artifactId> <version>1.7.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.7.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> <version>5.7.1</version> <scope>test</scope> </dependency>
하지만 결과는 마찬가지였고… .m2폴더에서 Junit 의존성을 다 삭제하고, Intellij project setting에서 Modules, Libraies에서 모두 삭제하고 이것저것 다 해봤지만 모두 실패로 돌아가서 결국 새 maven 프로젝트를 만들어서 하나씩 비교해보기로 했다.
publicstaticvoidmain(String[] args)throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
List<Integer> differences = new ArrayList<>(); List<Integer> list = new ArrayList<>();
int preValue = 0;
int n = Integer.parseInt(br.readLine()); while (n-- > 0) { int input = Integer.parseInt(br.readLine()); list.add(input);
if (preValue != 0) { differences.add(Math.abs(preValue - input)); }
preValue = input; }
Collections.sort(list);
int gcd = Integer.MAX_VALUE; for (int i = 1; i < differences.size(); i++) { int temp = gcd(differences.get(i-1), differences.get(i)); gcd = temp > gcd ? gcd : temp; }
int answer = 0; int pre = list.get(0); for (int i = 1; i < list.size(); i++) { if (list.get(i) - pre != gcd) { answer += ((list.get(i) - pre) / gcd) - 1; } pre = list.get(i); }
System.out.println(answer); }
privatestaticintgcd(int a, int b){ return b!=0 ? gcd(b, a%b) : a; }
흐름
입력 받은 값들을 뺀 값의 차를 구한 값으로 약수를 구하기 위한 differences, 입력 받은 값을 그대로 저장하는 list 변수 생성.
두 리스트 모두 n 만큼 돌면서 값을 저장하는데
differences의 경우 첫 번쨰 루프에서 값을 저장 할 수 없으므로(2 번쨰 값부터 첫 번째 값과 뺄 수 있으므로) 이전 값이 있는 경우에만 리스트에 저장한다.
가로수들 사이에 끼워넣어야 되는 가로수의 개수를 구해야 하므로 입력 받은 가로수의 위치를 정렬한다.
가로수 간의 최소 간격을 구하기 위해 아까 저장한 가로수 간의 차이를 구한 리스트 differences를 돌면서 유클리드 호제법을 이용해 최대공약수를 구하고, 구한 최대 공약수 중 가장 작은 값을 저장한다.
가로수 간의 차이값에서 최대 공약수를 구하는 이유는,
문제가 가로수를 일정한 간격으로 최대한 적게 심기게 하기 위한 문제 이므로 두 수에서 공통되는 값들 중 가장 큰 수인 최대 공약수를 구하고,
그 최대 공약수들 중 가장 작은 값이어야 모두 일정한 간격으로 심을 수 있으므로 최대 공약수 중 가장 작을 값을 구한다.
이렇게 잘 구한 값으로 이제 몇 개의 가로수를 더 심어야 하는지 알아내야 하므로
아까 정렬시킨 가로수 리스트를 반복 하면서 이전 가로수 와의 간격이 최대 공약수 보다 큰 녀석들이 있으면
publicstaticvoidmain(String[] args)throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine());
Map<String, Boolean> isDuplicate = new HashMap<>(); Map<String, Integer> domJudge = new HashMap<>();
for (int i = 0; i < n; i++) { String input = br.readLine();
if (!isDuplicate.containsKey(input)) { isDuplicate.put(input, false); }
publicstaticvoidmain(String[] args)throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(br.readLine());
LinkedList<Integer> queue = new LinkedList<>(); int dasom = Integer.parseInt(br.readLine()); for (int i = 0; i < n-1; i++) { int input = Integer.parseInt(br.readLine());
if (dasom <= input) { queue.offer(input); } }
Collections.sort(queue);
int answer = 0; for (; !queue.isEmpty() && queue.peekLast() >= dasom; answer++) { if (queue.peek() < dasom) { queue.poll(); }
queue.offer((queue.pollLast()-1)); dasom++;
Collections.sort(queue); }
System.out.println(answer); }
흐름
다솜이는 기호 1번이므로 첫 번째 입력을 다솜이의 표로 저장한다.
우선순위 큐로 사용하기 위해 list에 입력들을 저장하는데 다솜이 보다 작을 경우엔 문제의 답과 상관이 없으므로 다솜이의 표보다 적은 표는 큐에 저장하지 않는다.
우선순위 큐로서 동작하게 하기 위해 반복 전에 큐를 한번 정렬시켜준다.
그 후엔 큐가 비거나, 큐의 가장 큰 값이 다솜이가 될 때까지 반복한다.
반복하면서 큐의 맨 앞이 다솜이보다 작다면, 이제 그 사람은 다솜이가 재낀 것 이므로 큐에서 빼주고,
제일 표가 많은 놈한테서 표 하나를 뻇어서 다솜이가 가져가면 되므로 큐의 마지막 값에서 -1 빼서 다시 큐에 넣어준다.
그리고 제일 표가 많은 놈에게 뺏은 표를 다솜이에게 줬으므로 다솜이의 값을 1 증가시키고
우선순위 큐 처럼 동작 할 수 있게 큐를 다시 정렬 시킨다.
반복된 횟수를 출력하면 끝.
결과
여담
반복문에서 소팅을 하기 때문에 시간 초과가 날 것으로 예상했는데 수가 적은 문제라 그런지 무사히 통과되어 참 다행이다…
Front를 Vue.js, Back을 Spring Boot로 만든 토이 프로젝트에서 CORS로 인해 통신이 되지 않는 오류가 발생하여 문제를 해결한 방법을 작성 해둔다.
문제 상황
화면에서 체크박스를 클릭하면 서버로 requert를 보내고 서버에서 그에 맞는 response를 주는 방식에 간단한 프로젝트 인데 클릭 시 아래 이미지 처럼 ‘Network Error’라는 alert를 발생시키고 통신이 되지 않는 문제가 발생 하였고, 개발자 도구로 콘솔을 확인해보니 아래 이미지 처럼 ‘CORS Preflight Did Not Succeed’과
사실, 제목이 결론이다. @Requestbody Annotation을 사용하려면 반드시 해당 DTO에는 기본 생성자가 명시적으로 존재하여야 한다. 뭔가 설정을 잘못했는지는 모르겠지만 intellij 에서 stacktrace가 출력되지 않아 이 사실을 알 수 가 없어서 엄청나게 삽질을 했기에 내용을 정리한다…
본론
상황
controller 테스트에서 모든 조건을 맞췄는데도 500 에러가 발생하여 테스트를 통과하지 못하는 상황이었는데 @Requestbody annotation을 DTO가 아닌 HashMap으로 변경하니 테스트가 정상적으로 통과되어 JSON이 제대로 DTO로 매핑되지 않아서 발생하는 문제라고 판단했다.
에러 발생
No suitable constructor found for type [simple type, class 클래스명]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
직역하면 기본 생성자가 존재하지 않아서 에러가 발생했다는 내용이다.
해결 방법
빌더패턴을 사용하던 클래스에 @Noargsconstructor annotation을 사용해서 기본 생성자를 생성하게 하였다.
결론
@Requestbody와 DTO를 매핑되게 해야 할 경우 많은 조건이 필요하다. 이전 포스트에 작성했던 boolean의 변수명이라던가, 지금과 같은 기본 생성자라 라던지… 이게 모두 @Requestbody가 jackson 라이브러리를 이용해 매핑하기 때문이다.
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.jjlee.wedding.payment.domain.Cost.costOptions, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383) at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375) at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:122) at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248) at com.xxxxxx.ImplTest.가져와서_셋해야하는지_테스트(CostServiceImplTest.java:89) 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:59) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) 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:366) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329) at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293) 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$3.evaluate(ParentRunner.java:306) at org.junit.runners.ParentRunner.run(ParentRunner.java:413) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
이 때 디버깅을 해보면 연결된 entitiy에서
1
Unable to evaluate the expression Method threw 'org.hibernate.LazyInitializationException' exception.
라는 에러가 발생해 있는데 이는 결국 select 한 entitiy가 영속성 컨텍스트 내에 존재하지 않기 때문에 발생한 애러이다. 위 에러에서 말하는 세션이 바로 영속성 컨텍스트를 말하는 것이고, 이는 한 트랜잭션안에 해당 entitiy가 존재하지 않다는 것과 같은 말이다.
해결
이를 해결하기 위해선 현재 지연로딩으로 되어있는 연관관계를 즉시로딩으로 변경하여 한번에 가져오던가, 혹은 Test 메서드에 @Transactional 어노테이션을 줘서 트랜잭션 내에 존재하도록 해주면 테스트가 정상적으로 통과되게 된다.