Вопрос по java, junit, console – Тест JUnit для System.out.println ()

312

Мне нужно написать тесты JUnit для старого приложения, которое плохо спроектировано и записывает много сообщений об ошибках в стандартный вывод. КогдаgetResponse(String request) Метод ведет себя правильно, он возвращает ответ XML:

@BeforeClass
public static void setUpClass() throws Exception {
    Properties queries = loadPropertiesFile("requests.properties");
    Properties responses = loadPropertiesFile("responses.properties");
    instance = new ResponseGenerator(queries, responses);
}

@Test
public void testGetResponse() {
    String request = "<some>request</some>";
    String expResult = "<some>response</some>";
    String result = instance.getResponse(request);
    assertEquals(expResult, result);
}

Но когда он получает неправильно сформированный XML или не понимает запрос, он возвращаетnull и записывает некоторые вещи в стандартный вывод.

Есть ли способ утверждать вывод консоли в JUnit? Чтобы ловить такие случаи, как:

System.out.println("match found: " + strExpr);
System.out.println("xml not well formed: " + e.getMessage());
Связанный, но не дубликатstackoverflow.com/questions/3381801/… Raedwald

Ваш Ответ

12   ответов
1

потому что он перенаправляет для ВСЕЙ JVM. Все, что работает на JVM, может испортиться. Есть лучшие способы для проверки ввода / вывода. Посмотрите на заглушки / издевательства.

21

накрытый стол () (и дляin а такжеerr). Можете ли вы перенаправить это в поток печати, который записывает в строку, а затем проверить это? Это будет самый простой механизм.

(Я бы посоветовал на каком-то этапе преобразовать приложение в какую-то среду ведения журналов - но я подозреваю, что вы уже знаете об этом!)

Это было то, что пришло мне в голову, но я не мог поверить, что не существует стандартного JUnit способа сделать это. Спасибо, Мозг. Но кредиты дошли до ДФА за реальные усилия. Mike Minicki
Да. Я бы поддержал это и, возможно, даже поставил бы вопрос об утверждении ведения журнала (лучше назначить вызов компоненту ведения журнала или тому подобному)
Этот подход чреват проблемами, поскольку стандартный поток вывода является общим ресурсом, используемым всеми частями вашей программы. Лучше использовать Dependency Injection, чтобы исключить прямое использование стандартного потока вывода:stackoverflow.com/a/21216342/545127
0

system.out.println или используяlogger api при использованииJUnit, Но если вы хотите проверить какие-либо значения, вы можете просто использовать

Assert.assertEquals("value", str);

Будет выдано сообщение об ошибке ниже:

java.lang.AssertionError: expected [21.92] but found [value]

Ваше значение должно быть 21,92. Теперь, если вы будете тестировать, используя это значение, как показано ниже, ваш тестовый пример пройдет.

 Assert.assertEquals(21.92, str);
90

Системные правила

Пример из документов:

public void MyTest {
    @Rule
    public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();

    @Test
    public void overrideProperty() {
        System.out.print("hello world");
        assertEquals("hello world", systemOutRule.getLog());
    }
}

Это также позволит вам ловушкуSystem.exit(-1) и другие вещи, для которых инструмент командной строки должен был бы быть проверен.

Этот подход чреват проблемами, поскольку стандартный поток вывода является общим ресурсом, используемым всеми частями вашей программы. Лучше использовать Dependency Injection, чтобы исключить прямое использование стандартного потока вывода:stackoverflow.com/a/21216342/545127
0

@Test
void it_prints_out() {

    PrintStream save_out=System.out;final ByteArrayOutputStream out = new ByteArrayOutputStream();System.setOut(new PrintStream(out));

    System.out.println("Hello World!");
    assertEquals("Hello World!\r\n", out.toString());

    System.setOut(save_out);
}

для ошибки

@Test
void it_prints_err() {

    PrintStream save_err=System.err;final ByteArrayOutputStream err= new ByteArrayOutputStream();System.setErr(new PrintStream(err));

    System.err.println("Hello World!");
    assertEquals("Hello World!\r\n", err.toString());

    System.setErr(save_err);
}
Для такого рода настройки и логики демонтажа я бы использовал@Ruleвместо того, чтобы делать это в своем тесте. Примечательно, что если ваше утверждение не удастсяSystem.setOut/Err вызов будетnot быть достигнутым.
10

но в случае, если некоторые люди (например, я, когда я впервые нашел эту тему) могут быть заинтересованы в захвате вывода журнала через SLF4J,Обще-тестированиеJUnit@Rule может помочь:

public class FooTest {
    @Rule
    public final ExpectedLogs logs = new ExpectedLogs() {{
        captureFor(Foo.class, LogLevel.WARN);
    }};

    @Test
    public void barShouldLogWarning() {
        assertThat(logs.isEmpty(), is(true)); // Nothing captured yet.

        // Logic using the class you are capturing logs for:
        Foo foo = new Foo();
        assertThat(foo.bar(), is(not(nullValue())));

        // Assert content of the captured logs:
        assertThat(logs.isEmpty(), is(false));
        assertThat(logs.contains("Your warning message here"), is(true));
    }
}

Disclaimer:

I developed this library since I could not find any suitable solution for my own needs. Only bindings for log4j, log4j2 and logback are available at the moment, but I am happy to add more.
Большое спасибо за создание этой библиотеки! Я долго искал что-то подобное! Это очень и очень полезно, поскольку иногда вы просто не можете упростить свой код настолько, чтобы его можно было легко проверить, но с помощью сообщения журнала вы можете творить чудеса!
Это выглядит действительно многообещающе ... но даже когда я просто копирую вашу программу ATMTest и запускаю ее в качестве теста под Gradle, я получаю исключение ... Я поднял проблему на вашей странице Github ...
1

System.out (заменить часть когда):

package learning;

import static org.assertj.core.api.BDDAssertions.then;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class SystemOutLT {

    private PrintStream originalSystemOut;
    private ByteArrayOutputStream systemOutContent;

    @BeforeEach
    void redirectSystemOutStream() {

        originalSystemOut = System.out;

        // given
        systemOutContent = new ByteArrayOutputStream();
        System.setOut(new PrintStream(systemOutContent));
    }

    @AfterEach
    void restoreSystemOutStream() {
        System.setOut(originalSystemOut);
    }

    @Test
    void shouldPrintToSystemOut() {

        // when
        System.out.println("example");

        then(systemOutContent.toString()).containsIgnoringCase("example");
    }
}
6

что вы работаете со старым приложением, так что вы, вероятно, не используете его, но он может быть полезен для других), то вы можете использоватьorg.springframework.boot.test.rule.OutputCapture следующим образом:

@Rule
public OutputCapture outputCapture = new OutputCapture();

@Test
public void out() {
    System.out.print("hello");
    assertEquals(outputCapture.toString(), "hello");
}
Я проголосовал за ваш ответ, потому что я использую загрузку Spring, и это поставило меня на правильный путь. Спасибо! Однако outputCapture необходимо инициализировать. (public OutputCapture outputCapture = new OutputCapture ();) См.docs.spring.io/spring-boot/docs/current/reference/html/…
Вы абсолютно правы. Спасибо за комментарий! Я обновил свой ответ.
19

System.outЯ бы рефакторинг класса, который используетSystem.out.println() передаваяPrintStream в качестве сотрудника, а затем с помощьюSystem.out в производстве иTest Spy в тесте. То есть используйте Dependency Injection, чтобы исключить прямое использование стандартного потока вывода.

In Production

ConsoleWriter writer = new ConsoleWriter(System.out));

In the Test

ByteArrayOutputStream outSpy = new ByteArrayOutputStream();
ConsoleWriter writer = new ConsoleWriter(new PrintStream(outSpy));
writer.printSomething();
assertThat(outSpy.toString(), is("expected output"));

Discussion

Таким образом, тестируемый класс становится тестируемым с помощью простого рефакторинга, без необходимости косвенного перенаправления стандартного вывода или неясного перехвата с системным правилом.

Вероятно, следует упомянуть в ответе, но я считаю, что класс был создан пользователем 1909040.
Я не смог найти этот ConsoleWriter нигде в JDK: где он?
Я думаюConsoleWriter является испытуемым,
498

ByteArrayOutputStream и System.setXXX прост:

private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private final ByteArrayOutputStream errContent = new ByteArrayOutputStream();
private final PrintStream originalOut = System.out;
private final PrintStream originalErr = System.err;

@Before
public void setUpStreams() {
    System.setOut(new PrintStream(outContent));
    System.setErr(new PrintStream(errContent));
}

@After
public void restoreStreams() {
    System.setOut(originalOut);
    System.setErr(originalErr);
}

Примеры тестовых случаев:

@Test
public void out() {
    System.out.print("hello");
    assertEquals("hello", outContent.toString());
}

@Test
public void err() {
    System.err.print("hello again");
    assertEquals("hello again", errContent.toString());
}

Я использовал этот код для проверки опции командной строки (утверждая, что -version выводит строку версии и т. Д. И т. Д.)

Edit: Предыдущие версии этого ответа называютсяSystem.setOut(null) после испытаний; Это является причиной обращения к комментаторам NullPointerExceptions.

Я предпочитаю использовать System.setOut (null), чтобы восстановить поток обратно к тому, что было при запуске виртуальной машины
Javadocs ничего не говорит о возможности передать значение null в System.setOut или System.setErr. Вы уверены, что это будет работать на всех JRE?
Отличное решение. Просто примечание для тех, кто его использует, вам может понадобиться trim () пробел / символ новой строки из outContent.
Я столкнулся сNullPointerException в других тестах после установки нулевого потока ошибок, как предложено выше (вjava.io.writer(Object), вызывается изнутри валидатором XML). Я бы предложил вместо сохранения оригинала в поле:oldStdErr = System.err и восстановить это в@After метод.
Кроме того, я использовал JUnitMatchers для проверки ответов: assertThat (result, containsString (& quot; & lt; request: GetEmployeeByKeyResponse & quot;)); Спасибо, дфа. Mike Minicki
7

поэтому я сделал еще один шаг, чтобы можно было проверить блоки вывода.

Сначала я создалTestHelper с методомcaptureOutput который принимает раздражающий классCaptureTest, Метод captureOutput выполняет установку и разбор выходных потоков. Когда реализацияCaptureOutput& APOS; stest вызывается метод, он имеет доступ к генерации вывода для тестового блока.

Источник для TestHelper:

public class TestHelper {

    public static void captureOutput( CaptureTest test ) throws Exception {
        ByteArrayOutputStream outContent = new ByteArrayOutputStream();
        ByteArrayOutputStream errContent = new ByteArrayOutputStream();

        System.setOut(new PrintStream(outContent));
        System.setErr(new PrintStream(errContent));

        test.test( outContent, errContent );

        System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out)));
        System.setErr(new PrintStream(new FileOutputStream(FileDescriptor.out)));

    }
}

abstract class CaptureTest {
    public abstract void test( ByteArrayOutputStream outContent, ByteArrayOutputStream errContent ) throws Exception;
}

Обратите внимание, что TestHelper и CaptureTest определены в одном файле.

Затем в своем тесте вы можете импортировать статический captureOutput. Вот пример использования JUnit:

// imports for junit
import static package.to.TestHelper.*;

public class SimpleTest {

    @Test
    public void testOutput() throws Exception {

        captureOutput( new CaptureTest() {
            @Override
            public void test(ByteArrayOutputStream outContent, ByteArrayOutputStream errContent) throws Exception {

                // code that writes to System.out

                assertEquals( "the expected output\n", outContent.toString() );
            }
        });
}
1

ответ @ dfa а такжееще один ответ, который показывает, как проверить System.inЯ хотел бы поделиться своим решением, чтобы дать вход для программы и проверить ее вывод.

В качестве ссылки я использую JUnit 4.12.

Допустим, у нас есть эта программа, которая просто копирует ввод в вывод:

import java.util.Scanner;

public class SimpleProgram {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print(scanner.next());
        scanner.close();
    }
}

Чтобы проверить это, мы можем использовать следующий класс:

import static org.junit.Assert.*;

import java.io.*;

import org.junit.*;

public class SimpleProgramTest {
    private final InputStream systemIn = System.in;
    private final PrintStream systemOut = System.out;

    private ByteArrayInputStream testIn;
    private ByteArrayOutputStream testOut;

    @Before
    public void setUpOutput() {
        testOut = new ByteArrayOutputStream();
        System.setOut(new PrintStream(testOut));
    }

    private void provideInput(String data) {
        testIn = new ByteArrayInputStream(data.getBytes());
        System.setIn(testIn);
    }

    private String getOutput() {
        return testOut.toString();
    }

    @After
    public void restoreSystemInputOutput() {
        System.setIn(systemIn);
        System.setOut(systemOut);
    }

    @Test
    public void testCase1() {
        final String testString = "Hello!";
        provideInput(testString);

        SimpleProgram.main(new String[0]);

        assertEquals(testString, getOutput());
    }
}

Я не буду много объяснять, потому что я считаю, что код читабелен, и я привел свои источники.

Когда работает JUnittestCase1(), он будет вызывать вспомогательные методы в порядке их появления:

setUpOutput(), because of the @Before annotation provideInput(String data), called from testCase1() getOutput(), called from testCase1() restoreSystemInputOutput(), because of the @After annotation

Я не тестировалSystem.err потому что мне это не нужно, но оно должно быть простым в реализации, аналогично тестированиюSystem.out.

Похожие вопросы