The following unit test will pass repeatedly in both your IDE (ex: run
until failure) or in stressNewTest:

public class SystemOutRuleTest {

  @Rule
  public SystemOutRule systemOutRule = new SystemOutRule().enableLog();

  @Test
  public void passesRepeatedly() {
    System.out.println("hello");

    assertThat(systemOutRule.getLog()).contains("hello");
  }
}

But the following test will fail after the first time the test is executed
(ex: run until failure in IntelliJ) :

public class SystemOutRuleTest {

  @Rule
  public SystemOutRule systemOutRule = new SystemOutRule().enableLog();

  @Test
  public void passesFirstTime() {
    LogManager.getLogger().info("hello");

    assertThat(systemOutRule.getLog()).contains("hello");
  }
}

If you need to verify that a class is logging a specific message in some
circumstances then you can extract that test from the Unit Test and move it
to an Integration Test. You do *not* have to annotate SystemOutRule
with @ClassRule but I recommend doing that especially if you're using other
Rules doing some complicated setUp involving Geode classes:

public class CacheLoggingIntegrationTest {

  private InternalCache cache;

  @Rule
  public SystemOutRule systemOutRule = new SystemOutRule().enableLog();

  @Before
  public void setUp() {
    cache = (InternalCache) new CacheFactory().set(LOCATORS, "").create();
  }

  @After
  public void tearDown() {
    cache.close();
  }

  @Test
  public void passesFirstTime() {
    cache.getLogger().info("hello");

    assertThat(systemOutRule.getLog()).contains("hello");
  }
}

If you change the SystemOutRule in CacheLoggingIntegrationTest to a
static @ClassRule:

  @ClassRule
  public static SystemOutRule systemOutRule = new
SystemOutRule().enableLog();

...then it will repeatedly pass if you run-until-failure in IntelliJ but
only because IJ ends up running it as one test class. It will still fail
when run repeatedly by stressNewTest or if a previous test class caused
Log4J2 to startup within the same JVM.

On Mon, Nov 26, 2018 at 1:46 PM Kirk Lund <kl...@apache.org> wrote:

> Log4J and Logback both capture a reference to System.out and/or System.err
> and then use that reference thereafter. This means that any manipulation of
> System.out or System.err -- using System.setOut(PrintStream) or
> System.setErr(PrintStream) -- probably isn't going to play nicely with
> logging in general.
>
> Here's what that means for Geode tests...
>
> If you're writing a Unit Test, then that test will be executed in a
> long-lived JVM in which other Unit Tests are also going to be executed. The
> SystemOutRule from system-rules will work with code that uses System.out
> directly but will not work repeatedly with loggers.
>
> If you're writing an Integration Test or Distributed Test, then that test
> will be executed in a fresh JVM (fork every one is the config), so as long
> as you can get the SystemOutRule before to invoke before logging, the test
> will behave. In general that means you'll want to annotate it
> with @BeforeClass.
>
> Here are a couple examples in which log4j2 captures System.out to
> illustrate why SystemOutRule can't intercept the output after invoking
> System.setOut(PrintStream):
>
> log4j-core/src/main/java/org/apache/logging/log4j/core/config/status/StatusConfiguration.java:43:
>   private static final PrintStream DEFAULT_STREAM = System.out;
>
> log4j-api/src/main/java/org/apache/logging/log4j/simple/SimpleLoggerContext.java:87:
>           ps = System.out;
>
> If SystemOutRule executes its before -- System.setOut(PrintStream) --
> after the above code, logging will be printing to a different PrintStream
> than the PrintStream that SystemOutRule inserted into System.
>

Reply via email to