[완료] JAVA (다른 언어라도)에서 표준 출력으로 나오는 걸 가로채서 스트링으로 저장하려고 하는데...

raymundo의 이미지

http://kldp.org/node/80851

위 글타래를 통해서, JUnit을 사용하면 어떤 메쏘드가 제대로 된 결과를 리턴하는지 테스트를 일목요연하게 할 수 있겠다는 건 알았습니다.

그런데, 어떤 값을 리턴하는 메쏘드가 아니라, "리턴타입은 void이고, 뭔가를 표준출력으로 출력하는 메쏘드/함수"의 출력이 올바른지를 검사하고픈데 말이죠.

예: hello() 메쏘드는 표준출력으로 "hello"를 출력함
실제로 작성은 다음과 같이 했겠죠.
 
void hello() {
   System.out.println("hello");
}

이 때 hello()를 실행했는데 진짜로 "hello"가 출력되는지 검사하고 싶은 거죠. hello()의 코드는 전혀 건드리지 않고, 그걸 호출하는 쪽에서 수를 써야 합니다.

일단 떠오른 건 파이프를 사용하거나 리다이렉션을 써서 테스트하려는 메쏘드를 실행하고 그 결과를 파일로 저장한 후 그 파일을 읽는다..는 식인데, 이건 너무 불편합니다. 저렇게 검사하고픈 메쏘드가 여러 개 있으면 각각의 메쏘드에 대해서 그 메쏘드만 호출하는 프로그램을 먼저 작성하고 그 프로그램을 실행해야 하니까요. 메쏘드 하나 검사할 때마다 프로그램 따로 만들고 실행하여서 그걸 파이프로 받든가 리다이렉션을 써서 파일로 저장하던가 하는 것은 도저히... -_-;

어떤 꽁수(?)라도 써서,

String output;
hello();                    // 이렇게 호출했는데
if (output.equals("hello")) // output에 출력결과가 저장이 될 수 있다면!

아니면
String output = capture(hello());
// hello()가 void형인데... 설마 이렇게 쓰는 게 가능하지는 않으리라 생각합니다만..

처럼 할수만 있다면... output의 값을 정답과 비교해서 결과를 알 수 있을텐데요.

뭔가 방법이 없을까요? 거의 모든 출력은 System.out.print, println, printf 등을 쓸 테니, 저 System.out 으로 나가는 걸 가로채게 할 수 있으면 될 것 같은데...

.
.
.
.
.
.
.
.

여기서부터는 여담인데...

왜 이런 걸 필요로 하냐 하면... 매주마다 스무개 가량의 숙제 제출물 채점을 해야 하거든요. :-)

예년까지는 어떻게 했냐 하면, 모범 답안(?) 격인 클래스를 가지고 테스트를 하여 결과를 텍스트 파일로 저장하고, 각 학생이 제출한 클래스를 가지고 동일한 테스트 코드를 돌려서 그 결과를 역시 텍스트 파일로 저장한 후에, diff 로 비교하고 있습니다. diff의 종료값이 참이면 더 볼 것 없이 만점인거고, 그렇지 않으면 그때부터는 제가 눈으로 실제 출력을 보면서 어디가 잘못되었나를 봅니다.

뭐 대부분의 경우는 별 문제가 없습니다. 숙제가 크게 어려운 것도 아니고 해서 일단 거의 만점을 받는데다가, 출력이 다르게 나오는 경우도 보면 대소문자나 빈줄 포함 등이라... (diff에서는 이런 건 무시하게 할 수도 있으니 편하기까지 하죠) 웬만하면 diff에 걸려도 눈으로 쓰윽 보면 뭘 잘못했는지 금방 알 수 있긴 합니다만... 아래와 같은 단점들이 있더라고요.

1) 어떤 메쏘드가 exception을 발생시켜 버리면 테스트 프로그램이 죽어버리니 나머지 부분의 테스트를 못함 - 따라서 각각의 테스트 구간마다 try catch 로 감싸줘야 함

2) diff는 단지 텍스트를 비교할 뿐이지 이게 어떤 테스트의 결과인지 알지못하기 때문에, 비슷비슷한 출력이 반복되어 나오는 구간에서 차이점이 발생하면 제대로 비교를 못 해 줌 (첫번째 테스트의 마지막에 출력이 추가된 것을 두번째 테스트의 처음에 추가된 것으로 판단한다던가 등)

3) 결국 몇 번째 테스트에서 오류가 발생했는지는 제가 눈으로 숙제의 출력 또는 diff 결과를 보면서 찾아야 하는 것이라서, 점수 매길 때 불편합니다. JUnit이나 perl의 Test::More 처럼 정확히 "3번 테스트 실패"처럼 나오면 저는 "3번 테스트 틀렸으니 10점 감점" 이렇게 바로 점수를 매길 수 있겠죠. 심지어는 테스트 결과를 보고 감점까지 자동으로 해 주는 프로그램을 다시 짤 수도 있을테고요.

4) 이 글타래의 질문과 반대로, 이번에는 리턴값이 있는 메쏘드들을 테스트하기 위해서 일일이 그 리턴값을 표준출력으로 출력해주어야 함. 뭐 이게 어려운 건 아닙니다. System.out.println(method(인자)); 라고 적어주면 그만이니.

이런 이유로 "프로그램을 테스트하는 프로그램"이 필요해졌습니다. 그러고보면 여기 오시는 분들 중에도 저 같이 학생들 수십명이 제출한 코드를 각각 테스트해야 하는 경우를 겪는 분들이 없지 않을 것 같은데... 어떻게 하시나 궁금하네요 ^^

bootmeta의 이미지

http://coffeenix.net/doc/lpg/lpg_6_2_3.html
여기에 popen에 대해 자세한 설명이 있습니다.

popen은 기본 unix c함수에 속해서 다른 언어들에서도 많이 지원 할 겁니다.
위 사이트의 ls 결과를 sort하는 프로그램을 간단히 pseudo 코드로 줄여 봤습니다.

main()
{
   FILE * in;
   FILE * out;
   char buf[80];
 
   in = popen("ls", "r");
   out = popen("sort", "w"));
 
   while(fgets(readbuf, 80, in))
       fputs(readbuf, out);
 
   pclose(in);
   pclose(out);
}

펄을 쓰신다면 open할 때 뒤에 |만 추가하면 됩니다.
http://perl.active-venture.com/pod/perlopentut-openshell.html

 open(NET, "netstat -i -n |")    || die "can't fun netstat: $!";
    while (<NET>) { }               # do something with input
    close(NET)    

raymundo의 이미지

친절한 답변 감사드립니다.

저렇게 pipe를 써서 프로세스의 출력결과를 얻어오는 것의 문제점이... 제가 테스트하고싶은 메쏘드가 3개 a(), b(), c()가 있다면

a()만을 호출하는 프로그램 하나, b()만을 호출하는 프로그램 하나, c()만을 호출하는 프로그램 하나 이렇게 최소 3개(인자에 따라 출력이 달라지는 것까지 테스트하려면 더 필요할 수도)의 프로그램을 또 만들고 popen("a()를 호출는 프로그램")을 써서 결과를 가져오고 등등...하려면 배보다 배꼽이 너무 커진다는 거죠.

bootmeta님이 적어주신 코드에 비교하면, "ls"에 해당하는 프로그램이 메쏘드 갯수만큼 필요하다..라는 것이니..

뭐 프로그램 하나를 만들고 명령행인자로 옵션을 주어서 원하는 메쏘드만 실행하게 할 수도 있긴 하겠네요. 물론 popen은 옵션의 갯수만큼 있어야 하긴 마찬가지이지만, 적어도 a(),b(),c()각각을 호출하기 전에 준비과정이 필요하다면 그건 한번만 작성해주면 될테니 좀 낫겠죠. 여차하면 그렇게라도 할 생각을 하고 있습니다.

좋은 하루 되세요!

bootmeta의 이미지

최소한 open office는 python 연동이 되고, API가 공개되어 있으니 아마 인터넷에 누군가 올려놨을 것 같습니다.
어느 정도 프로그램 작성은 피할 수 없을 것 같군요.
예를 들어 첫 열에 모든 프로그램(인자 포함)에 넣어두고 run 시키면 2번째 열에 실행 결과, 3열에는 정답 여부 비교 결과를 출력할 수 있을 것 같습니다.

http://www.stuvel.eu/ooo-python에 간단히 python을 open office에서 실행하는 법
정답은 아니어도 자동화 관련 작업에 대한 언급이 있군요.
디렉토리 내 모든 파일 처리하는 법
자동으로 open office 실행하는 법

다시 한번 주제를 읽어보니 정작 중요한 부분들에 대해선 간과하고 있었군요.
결국 open office를 사용해도 출력만 보기 좋게 할 뿐 원하시는 결과가 안되겠네요.

1. try catch를 사용할 필요없이 JUnit는 exeption 발생시 자동으로 처리하는 class를 지정할 수 있는 것으로 알고 있습니다.

@Test(expected = Exception.class) 

2. 단위 테스트 프로그램에서 임시로 System.setout()으로 다른 파일등에 기록하고 stdout 원상 복귀
System.setOut(new PrintStream(new FileOutputStream(fileName + dialog.getWorkstationId() + ".out.log", true)));

좋은 결과가 나온 후 그 방법을 공개해주신다면 많은 도움이 될 겁니다.

raymundo의 이미지

System.out 을 뭔가 조작해줄 수 있을 것 같은데..라고 생각하면서, 왜 System 문서를 보면서 setOut()은 못 보고 지나갔을까요 OTL

일단 방금까지 작성한 코드입니다.

import java.io.*;
 
public class Test {
   private int count = 0;
 
   public void hello() {
      // 표준 출력으로 출력하는 메쏘드
      // hello() 자체는 건드리지 않음
      count++;
      System.out.println(count+"hello");
   }
 
   public static void main(String args[]) throws FileNotFoundException {
      Test t = new Test();
 
      // 표준 출력으로 출력
      t.hello(); // 터미널로 "1hello\n" 출력
 
 
      // 표준 출력 백업해 두고
      PrintStream stdout = System.out;
 
 
      // ByteArrayOutputStream 으로 리다이렉션하고
      ByteArrayOutputStream bo = new ByteArrayOutputStream();
      System.setOut(new PrintStream(bo, true));
      // hello() 실행해보고
      t.hello();
      // 다시 터미널로 출력하도록 복원
      System.setOut(stdout);
 
 
      // t.hello()의 결과는 bo에 담겨있네~~ ^o^
      // 아래처럼 if로 테스트할 수도 있으니, JUnit을 사용할 수도 있을 듯
      if (bo.toString().equals("2hello\n")) {
         System.out.println("ok");
      } else {
         System.out.println("not ok");
      }
   }
}

다시 한번 감사드립니다 ^^

좋은 하루 되세요!

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.