백준 문제 실행 테스트 환경(JAVA) 만들기
이 글을 포스트 한 이후 프로그래머스 문제를 실행 테스트 하기 위하여 코드를 리팩토링 하였다. 코딩 테스트를 준비하는 사람일 경우 프로그래머스 또한 연습해야 하는 사이트임에 분명하므로, 이 글이 아닌 링크 걸린 글을 확인하길 바란다.
[백준, 프로그래머스 문제 실행 테스트환경 (JAVA) 만들기 :: 꿀잠 (tistory.com)](https://ygs3004.tistory.com/17)
코딩테스트, 알고리즘을 위한 사이트들이 있는데 그 중 내가 이용하는 것은 프로그래머스와 백준 사이트이다.
프로그래머스의 경우 자체적으로 코드 실행을 할 수도 있고, 코드 실행중에 System.out.println 을 이용하여 디버깅도 나름 가능하다.
하지만 백준의 경우 디버깅을 하기가 힘들다. 또한 문제를 풀다가 왜 틀렸는지 알 수 없어 질문하기 쪽을 보다 보니 input 데이터에 공백이 들어가 있는 것 같다는 대답을 들은 적도 있다. 실제로 공백을 처리하는 로직을 추가하였더니 통과된 경험도 있다.
그래서 공백까지 들어가있는 테스트 데이터를 만들거나, InputStream 으로 input 데이터가 들어오는 문제를 실행 및 테스트를 할 수 있는 코드를 만들었고 이를 공유하고자 이 글을 포스트한다. 해당 코드는 아래와 같다.
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class Main {
public static void main(String[] args) throws Exception {
test(new Form());
}
private static void test(Object problem) throws Exception {
URL classDir = problem.getClass().getResource("");
File[] files = new File(classDir.toURI()).listFiles();
// Result 저장
Map<String, String> result = new HashMap();
Arrays.stream(files).filter((file) -> file.getName().startsWith("result")).forEach(file -> {
String fileName = file.getName();
String testSeq = fileName.substring(fileName.indexOf("result") + "result".length(), fileName.lastIndexOf("."));
StringBuilder resultSb = new StringBuilder();
try {
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
String line = "";
while((line = br.readLine()) != null){
resultSb.append(line).append(System.lineSeparator());
}
result.put(testSeq, resultSb.toString().trim());
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
AtomicBoolean isSuccess = new AtomicBoolean();
isSuccess.set(true);
// Input 실행
Arrays.stream(files).forEach(file -> {
String fileName = file.getName();
boolean isInput = fileName.startsWith("input");
if(isInput){
String testSeq = fileName.substring(fileName.indexOf("input") + "input".length(), fileName.lastIndexOf("."));
System.out.println("** " + testSeq + "번 테스트 실행 **");
try {
long startTime = System.nanoTime();
String testResult
= (String) problem.getClass()
.getDeclaredMethod("solution", InputStream.class)
.invoke(problem, new FileInputStream(file));
long endTime = System.nanoTime();
System.out.println("실행시간: " + (endTime-startTime)/1_000_000.00 +"ms");
System.out.println();
String expectResult = result.get(testSeq);
System.out.println("정답");
System.out.println(expectResult);
System.out.println();
System.out.println("결과");
System.out.println(testResult);
System.out.println();
if(testResult.equals(expectResult)){
System.out.println(testSeq + "번 테스트 성공");
isSuccess.set(isSuccess.get());
}else{
System.out.println(testSeq + "번 테스트 실패");
isSuccess.set(false);
}
} catch (IllegalAccessException | FileNotFoundException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
System.out.println("solution 메서드가 없습니다");
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
System.out.println("=======================================================================================");
}
});
if(isSuccess.get()){
System.out.println("모든 테스트가 통과하였습니다.");
}else{
System.out.println("************** 실패한 테스트가 있습니다. 결과를 확인해주세요 **************");
}
}
}
해당 코드를 활용하기 위해서는 우선 문제 풀이를 작성할 Form 클래스(클래스 이름은 문제마다 또는 원하는 형태)를 하나 만든다.
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Form {
public String solution(InputStream systemIn) throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(systemIn));
StringBuilder result = new StringBuilder();
// 문제풀이코드
System.out.println(result);
return result.toString();
}
}
문제 풀이 코드는 위와 같은 형태로 작성한다. 테스트 데이터는 input1.txt, result1.txt 형태로(input/result + 케이스 번호) 텍스트 파일을 만들어 문제 풀이 클래스와 동일한 디렉토리에 저장한다. 아래와 같은 형태면 된다.
프로젝트 루트
├── package1
│ ├── 문제1 Class
│ └── input1.txt
│ └── input2.txt
│ └── input3.txt
│ └── result1.txt
│ └── result2.txt
│ └── result33.txt
├── package2
│ ├── 문제2 Class
│ └── input1.txt
│ └── result1.txt
└── Main
if(isSuccess.get()){
System.out.println("모든 테스트가 통과하였습니다.");
}else{
System.out.println("************** 실패한 테스트가 있습니다. 결과를 확인해주세요 **************");
}
테스트 코드의 성공 여부에 따라 위와같은 멘트가 콘솔창에 출력된다.
폴더 구조 및 출력멘트는 Main 클래스에서 입맛에 맞게 변경하면 될 것이다.
백준에 제출하기 전에 내 PC 에서 실행하던 클래스를 적절히 변경 후 제출하면 된다.
// 실행환경 패키지 이름 제거
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
// public class Form { 임의의 클래스명 -> Main
public class Main {
// public String solution(InputStream systemIn) throws Exception{
public static void main(String[] args) throws Exception{
// systemIn => System.in
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder result = new StringBuilder();
// 문제풀이코드
System.out.println(result);
// return result.toString();
}
}
- package 이름 제거
- 클래스 이름 Main 으로 변경,
- main 메서드 이름 변경
- return 제거 후 정답 코드 출력(System.out.println 이 아니라 BufferWriter 등을 사용해도 된다.)
이후 런타임 오류가 없는데 문제가 틀린다면 문제를 다시 풀어보도록 하면된다.
열공하는 사람 모두 화이팅!